aboutsummaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_ra_serf/ra_serf.h
blob: 335a9e397e0dff7dd7aeaa66b664d3c59ca10c5e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
/*
 * ra_serf.h : Private declarations for the Serf-based DAV RA module.
 *
 * ====================================================================
 *    Licensed to the Apache Software Foundation (ASF) under one
 *    or more contributor license agreements.  See the NOTICE file
 *    distributed with this work for additional information
 *    regarding copyright ownership.  The ASF licenses this file
 *    to you under the Apache License, Version 2.0 (the
 *    "License"); you may not use this file except in compliance
 *    with the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing,
 *    software distributed under the License is distributed on an
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *    KIND, either express or implied.  See the License for the
 *    specific language governing permissions and limitations
 *    under the License.
 * ====================================================================
 */

#ifndef SVN_LIBSVN_RA_SERF_RA_SERF_H
#define SVN_LIBSVN_RA_SERF_RA_SERF_H


#include <serf.h>
#include <expat.h>  /* for XML_Parser  */
#include <apr_uri.h>

#include "svn_types.h"
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_ra.h"
#include "svn_delta.h"
#include "svn_version.h"
#include "svn_dav.h"
#include "svn_dirent_uri.h"

#include "private/svn_dav_protocol.h"
#include "private/svn_subr_private.h"
#include "private/svn_editor.h"

#include "blncache.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* Enforce the minimum version of serf. */
#if !SERF_VERSION_AT_LEAST(1, 2, 1)
#error Please update your version of serf to at least 1.2.1.
#endif

/** Use this to silence compiler warnings about unused parameters. */
#define UNUSED_CTX(x) ((void)(x))

/** Wait duration (in microseconds) used in calls to serf_context_run() */
#define SVN_RA_SERF__CONTEXT_RUN_DURATION 500000



/* Forward declarations. */
typedef struct svn_ra_serf__session_t svn_ra_serf__session_t;

/* A serf connection and optionally associated SSL context.  */
typedef struct svn_ra_serf__connection_t {
  /* Our connection to a server. */
  serf_connection_t *conn;

  /* Bucket allocator for this connection. */
  serf_bucket_alloc_t *bkt_alloc;

  /* Collected cert failures in chain.  */
  int server_cert_failures;

  /* What was the last HTTP status code we got on this connection? */
  int last_status_code;

  /* Optional SSL context for this connection. */
  serf_ssl_context_t *ssl_context;
  svn_auth_iterstate_t *ssl_client_auth_state;
  svn_auth_iterstate_t *ssl_client_pw_auth_state;

  svn_ra_serf__session_t *session;

} svn_ra_serf__connection_t;

/** Maximum value we'll allow for the http-max-connections config option.
 *
 * Note: minimum 2 connections are required for ra_serf to function
 * correctly!
 */
#define SVN_RA_SERF__MAX_CONNECTIONS_LIMIT 8

/*
 * The master serf RA session.
 *
 * This is stored in the ra session ->priv field.
 */
struct svn_ra_serf__session_t {
  /* Pool for allocations during this session */
  apr_pool_t *pool;

  /* The current context */
  serf_context_t *context;

  /* The maximum number of connections we'll use for parallelized
     fetch operations (updates, etc.) */
  apr_int64_t max_connections;

  /* Are we using ssl */
  svn_boolean_t using_ssl;

  /* Should we ask for compressed responses? */
  svn_boolean_t using_compression;

  /* The user agent string */
  const char *useragent;

  /* The current connection */
  svn_ra_serf__connection_t *conns[SVN_RA_SERF__MAX_CONNECTIONS_LIMIT];
  int num_conns;
  int cur_conn;

  /* The URL that was passed into _open() */
  apr_uri_t session_url;
  const char *session_url_str;

  /* The actual discovered root; may be NULL until we know it. */
  apr_uri_t repos_root;
  const char *repos_root_str;

  /* The server is not Apache/mod_dav_svn (directly) and only supports
     HTTP/1.0. Thus, we cannot send chunked requests.  */
  svn_boolean_t http10;

  /* Should we use Transfer-Encoding: chunked for HTTP/1.1 servers. */
  svn_boolean_t using_chunked_requests;

  /* Do we need to detect whether the connection supports chunked requests?
     i.e. is there a (reverse) proxy that does not support them?  */
  svn_boolean_t detect_chunking;

  /* Our Version-Controlled-Configuration; may be NULL until we know it. */
  const char *vcc_url;

  /* Authentication related properties. */
  svn_auth_iterstate_t *auth_state;
  int auth_attempts;

  /* Callback functions to get info from WC */
  const svn_ra_callbacks2_t *wc_callbacks;
  void *wc_callback_baton;

  /* Callback function to send progress info to the client */
  svn_ra_progress_notify_func_t progress_func;
  void *progress_baton;

  /* Callback function to handle cancellation */
  svn_cancel_func_t cancel_func;
  void *cancel_baton;

  /* Ev2 shim callbacks */
  svn_delta_shim_callbacks_t *shim_callbacks;

  /* Error that we've received but not yet returned upstream. */
  svn_error_t *pending_error;

  /* List of authn types supported by the client.*/
  int authn_types;

  /* Maps SVN_RA_CAPABILITY_foo keys to "yes" or "no" values.
     If a capability is not yet discovered, it is absent from the table.
     The table itself is allocated in the svn_ra_serf__session_t's pool;
     keys and values must have at least that lifetime.  Most likely
     the keys and values are constants anyway (and sufficiently
     well-informed internal code may just compare against those
     constants' addresses, therefore). */
  apr_hash_t *capabilities;

  /* Activity collection URL.  (Cached from the initial OPTIONS
     request when run against HTTPv1 servers.)  */
  const char *activity_collection_url;

  /* Are we using a proxy? */
  svn_boolean_t using_proxy;

  const char *proxy_username;
  const char *proxy_password;
  int proxy_auth_attempts;

  /* SSL server certificates */
  svn_boolean_t trust_default_ca;
  const char *ssl_authorities;

  /* Repository UUID */
  const char *uuid;

  /* Connection timeout value */
  apr_interval_time_t timeout;

  /* HTTPv1 flags */
  svn_tristate_t supports_deadprop_count;

  /*** HTTP v2 protocol stuff. ***
   *
   * We assume that if mod_dav_svn sends one of the special v2 OPTIONs
   * response headers, it has sent all of them.  Specifically, we'll
   * be looking at the presence of the "me resource" as a flag that
   * the server supports v2 of our HTTP protocol.
   */

  /* The "me resource".  Typically used as a target for REPORTs that
     are path-agnostic.  If we have this, we can speak HTTP v2 to the
     server.  */
  const char *me_resource;

  /* Opaque URL "stubs".  If the OPTIONS response returns these, then
     we know we're using HTTP protocol v2. */
  const char *rev_stub;         /* for accessing revisions (i.e. revprops) */
  const char *rev_root_stub;    /* for accessing REV/PATH pairs */
  const char *txn_stub;         /* for accessing transactions (i.e. txnprops) */
  const char *txn_root_stub;    /* for accessing TXN/PATH pairs */
  const char *vtxn_stub;        /* for accessing transactions (i.e. txnprops) */
  const char *vtxn_root_stub;   /* for accessing TXN/PATH pairs */

  /* Hash mapping const char * server-supported POST types to
     disinteresting-but-non-null values. */
  apr_hash_t *supported_posts;

  /*** End HTTP v2 stuff ***/

  svn_ra_serf__blncache_t *blncache;

  /* Trisate flag that indicates user preference for using bulk updates
     (svn_tristate_true) with all the properties and content in the
     update-report response. If svn_tristate_false, request a skelta
     update-report with inlined properties. If svn_tristate_unknown then use
     server preference. */
  svn_tristate_t bulk_updates;

  /* Indicates if the server wants bulk update requests (Prefer) or only
     accepts skelta requests (Off). If this value is On both options are
     allowed. */
  const char *server_allows_bulk;

  /* Indicates if the server supports sending inlined props in update editor
   * in skelta mode (send-all == 'false'). */
  svn_boolean_t supports_inline_props;

  /* Indicates whether the server supports issuing replay REPORTs
     against rev resources (children of `rev_stub', elsestruct). */
  svn_boolean_t supports_rev_rsrc_replay;
};

#define SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(sess) ((sess)->me_resource != NULL)

/*
 * Structure which represents a DAV element with a NAMESPACE and NAME.
 */
typedef struct svn_ra_serf__dav_props_t {
  /* Element namespace */
  const char *namespace;
  /* Element name */
  const char *name;
} svn_ra_serf__dav_props_t;

/*
 * Structure which represents an XML namespace.
 */
typedef struct ns_t {
  /* The assigned name. */
  const char *namespace;
  /* The full URL for this namespace. */
  const char *url;
  /* The next namespace in our list. */
  struct ns_t *next;
} svn_ra_serf__ns_t;

/*
 * An incredibly simple list.
 */
typedef struct ra_serf_list_t {
  void *data;
  struct ra_serf_list_t *next;
} svn_ra_serf__list_t;

/** DAV property sets **/

static const svn_ra_serf__dav_props_t base_props[] =
{
  { "DAV:", "version-controlled-configuration" },
  { "DAV:", "resourcetype" },
  { SVN_DAV_PROP_NS_DAV, "baseline-relative-path" },
  { SVN_DAV_PROP_NS_DAV, "repository-uuid" },
  { NULL }
};

static const svn_ra_serf__dav_props_t checked_in_props[] =
{
  { "DAV:", "checked-in" },
  { NULL }
};

static const svn_ra_serf__dav_props_t baseline_props[] =
{
  { "DAV:", "baseline-collection" },
  { "DAV:", SVN_DAV__VERSION_NAME },
  { NULL }
};

static const svn_ra_serf__dav_props_t all_props[] =
{
  { "DAV:", "allprop" },
  { NULL }
};

static const svn_ra_serf__dav_props_t check_path_props[] =
{
  { "DAV:", "resourcetype" },
  { NULL }
};

static const svn_ra_serf__dav_props_t type_and_checksum_props[] =
{
  { "DAV:", "resourcetype" },
  { SVN_DAV_PROP_NS_DAV, "sha1-checksum" },
  { NULL }
};

/* WC props compatibility with ra_neon. */
#define SVN_RA_SERF__WC_CHECKED_IN_URL SVN_PROP_WC_PREFIX "ra_dav:version-url"

/** Serf utility functions **/

apr_status_t
svn_ra_serf__conn_setup(apr_socket_t *sock,
                        serf_bucket_t **read_bkt,
                        serf_bucket_t **write_bkt,
                        void *baton,
                        apr_pool_t *pool);

void
svn_ra_serf__conn_closed(serf_connection_t *conn,
                         void *closed_baton,
                         apr_status_t why,
                         apr_pool_t *pool);


/* Helper function to provide SSL client certificates.
 *
 * NOTE: This function sets the session's 'pending_error' member when
 *       returning an non-success status.
 */
apr_status_t
svn_ra_serf__handle_client_cert(void *data,
                                const char **cert_path);

/* Helper function to provide SSL client certificate passwords.
 *
 * NOTE: This function sets the session's 'pending_error' member when
 *       returning an non-success status.
 */
apr_status_t
svn_ra_serf__handle_client_cert_pw(void *data,
                                   const char *cert_path,
                                   const char **password);


/*
 * This function will run the serf context in SESS until *DONE is TRUE.
 */
svn_error_t *
svn_ra_serf__context_run_wait(svn_boolean_t *done,
                              svn_ra_serf__session_t *sess,
                              apr_pool_t *scratch_pool);

/* Callback for response handlers */
typedef svn_error_t *
(*svn_ra_serf__response_handler_t)(serf_request_t *request,
                                   serf_bucket_t *response,
                                   void *handler_baton,
                                   apr_pool_t *scratch_pool);

/* Callback for when a request body is needed. */
/* ### should pass a scratch_pool  */
typedef svn_error_t *
(*svn_ra_serf__request_body_delegate_t)(serf_bucket_t **body_bkt,
                                        void *baton,
                                        serf_bucket_alloc_t *alloc,
                                        apr_pool_t *request_pool);

/* Callback for when request headers are needed. */
/* ### should pass a scratch_pool  */
typedef svn_error_t *
(*svn_ra_serf__request_header_delegate_t)(serf_bucket_t *headers,
                                          void *baton,
                                          apr_pool_t *request_pool);

/* Callback for when a response has an error. */
typedef svn_error_t *
(*svn_ra_serf__response_error_t)(serf_request_t *request,
                                 serf_bucket_t *response,
                                 int status_code,
                                 void *baton);

/* ### we should reorder the types in this file.  */
typedef struct svn_ra_serf__server_error_t svn_ra_serf__server_error_t;

/*
 * Structure that can be passed to our default handler to guide the
 * execution of the request through its lifecycle.
 */
typedef struct svn_ra_serf__handler_t {
  /* The HTTP method string of the request */
  const char *method;

  /* The resource to the execute the method on. */
  const char *path;

  /* The content-type of the request body. */
  const char *body_type;

  /* If TRUE then default Accept-Encoding request header is not configured for
     request. If FALSE then 'gzip' accept encoding will be used if compression
     enabled. */
  svn_boolean_t custom_accept_encoding;

  /* Has the request/response been completed?  */
  svn_boolean_t done;

  /* If we captured an error from the server, then this will be non-NULL.
     It will be allocated from HANDLER_POOL.  */
  svn_ra_serf__server_error_t *server_error;

  /* The handler and baton pair for our handler. */
  svn_ra_serf__response_handler_t response_handler;
  void *response_baton;

  /* When REPONSE_HANDLER is invoked, the following fields will be set
     based on the response header. HANDLER_POOL must be non-NULL for these
     values to be filled in. SLINE.REASON and LOCATION will be allocated
     within HANDLER_POOL.  */
  serf_status_line sline;  /* The parsed Status-Line  */
  const char *location;  /* The Location: header, if any  */

  /* The handler and baton pair to be executed when a non-recoverable error
   * is detected.  If it is NULL in the presence of an error, an abort() may
   * be triggered.
   */
  svn_ra_serf__response_error_t response_error;
  void *response_error_baton;

  /* This function and baton pair allows for custom request headers to
   * be set.
   *
   * It will be executed after the request has been set up but before it is
   * delivered.
   */
  svn_ra_serf__request_header_delegate_t header_delegate;
  void *header_delegate_baton;

  /* This function and baton pair allows a body to be created right before
   * delivery.
   *
   * It will be executed after the request has been set up but before it is
   * delivered.
   *
   * May be NULL if there is no body to send.
   *
   */
  svn_ra_serf__request_body_delegate_t body_delegate;
  void *body_delegate_baton;

  /* The connection and session to be used for this request. */
  svn_ra_serf__connection_t *conn;
  svn_ra_serf__session_t *session;

  /* Internal flag to indicate we've parsed the headers.  */
  svn_boolean_t reading_body;

  /* When this flag will be set, the core handler will discard any unread
     portion of the response body. The registered response handler will
     no longer be called.  */
  svn_boolean_t discard_body;

  /* Pool for allocating SLINE.REASON and LOCATION. If this pool is NULL,
     then the requestor does not care about SLINE and LOCATION.  */
  apr_pool_t *handler_pool;

} svn_ra_serf__handler_t;


/* Run one request and process the response.

   Similar to context_run_wait(), but this creates the request for HANDLER
   and then waits for it to complete.

   WARNING: context_run_wait() does NOT create a request, whereas this
   function DOES. Avoid a double-create.  */
svn_error_t *
svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
                             apr_pool_t *scratch_pool);


/*
 * Helper function to queue a request in the @a handler's connection.
 */
void svn_ra_serf__request_create(svn_ra_serf__handler_t *handler);

/* XML helper callbacks. */

typedef struct svn_ra_serf__xml_state_t {
  /* A numeric value that represents the current state in parsing.
   *
   * Value 0 is reserved for use as the default state.
   */
  int current_state;

  /* Private pointer set by the parsing code. */
  void *private;

  /* Allocations should be made in this pool to match the lifetime of the
   * state.
   */
  apr_pool_t *pool;

  /* The currently-declared namespace for this state. */
  svn_ra_serf__ns_t *ns_list;

  /* Our previous states. */
  struct svn_ra_serf__xml_state_t *prev;
} svn_ra_serf__xml_state_t;

/* Forward declaration of the XML parser structure. */
typedef struct svn_ra_serf__xml_parser_t svn_ra_serf__xml_parser_t;

/* Callback invoked with @a baton by our XML @a parser when an element with
 * the @a name containing @a attrs is opened.
 */
typedef svn_error_t *
(*svn_ra_serf__xml_start_element_t)(svn_ra_serf__xml_parser_t *parser,
                                    svn_ra_serf__dav_props_t name,
                                    const char **attrs,
                                    apr_pool_t *scratch_pool);

/* Callback invoked with @a baton by our XML @a parser when an element with
 * the @a name is closed.
 */
typedef svn_error_t *
(*svn_ra_serf__xml_end_element_t)(svn_ra_serf__xml_parser_t *parser,
                                  svn_ra_serf__dav_props_t name,
                                  apr_pool_t *scratch_pool);

/* Callback invoked with @a baton by our XML @a parser when a CDATA portion
 * of @a data with size @a len is encountered.
 *
 * This may be invoked multiple times for the same tag.
 */
typedef svn_error_t *
(*svn_ra_serf__xml_cdata_chunk_handler_t)(svn_ra_serf__xml_parser_t *parser,
                                          const char *data,
                                          apr_size_t len,
                                          apr_pool_t *scratch_pool);

/*
 * Helper structure associated with handle_xml_parser handler that will
 * specify how an XML response will be processed.
 */
struct svn_ra_serf__xml_parser_t {
  /* Temporary allocations should be made in this pool. */
  apr_pool_t *pool;

  /* What kind of response are we parsing? If set, this should typically
     define the report name.  */
  const char *response_type;

  /* Caller-specific data passed to the start, end, cdata callbacks.  */
  void *user_data;

  /* Callback invoked when a tag is opened. */
  svn_ra_serf__xml_start_element_t start;

  /* Callback invoked when a tag is closed. */
  svn_ra_serf__xml_end_element_t end;

  /* Callback invoked when a cdata chunk is received. */
  svn_ra_serf__xml_cdata_chunk_handler_t cdata;

  /* Our associated expat-based XML parser. */
  XML_Parser xmlp;

  /* Our current state. */
  svn_ra_serf__xml_state_t *state;

  /* Our previously used states (will be reused). */
  svn_ra_serf__xml_state_t *free_state;

  /* If non-NULL, this value will be set to TRUE when the response is
   * completed.
   */
  svn_boolean_t *done;

  /* If non-NULL, when this parser completes, it will add done_item to
   * the list.
   */
  svn_ra_serf__list_t **done_list;

  /* A pointer to the item that will be inserted into the list upon
   * completeion.
   */
  svn_ra_serf__list_t *done_item;

  /* If this flag is TRUE, errors during parsing will be ignored.
   *
   * This is mainly used when we are processing an error XML response to
   * avoid infinite loops.
   */
  svn_boolean_t ignore_errors;

  /* If an error occurred, this value will be non-NULL. */
  svn_error_t *error;

  /* Deciding whether to pause, or not, is performed within the parsing
     callbacks. If a callback decides to set this flag, then the loop
     driving the parse (generally, a series of calls to serf_context_run())
     is going to need to coordinate the un-pausing of the parser by
     processing pending content. Thus, deciding to pause the parser is a
     coordinate effort rather than merely setting this flag.

     When an XML parsing callback sets this flag, note that additional
     elements may be parsed (as the current buffer is consumed). At some
     point, the flag will be recognized and arriving network content will
     be stashed away in the PENDING structure (see below).

     At some point, the controlling loop should clear this value. The
     underlying network processing will note the change and begin passing
     content into the XML callbacks.

     Note that the controlling loop should also process pending content
     since the arriving network content will typically finish first.  */
  svn_boolean_t paused;

  /* While the XML parser is paused, content arriving from the server
     must be saved locally. We cannot stop reading, or the server may
     decide to drop the connection. The content will be stored in memory
     up to a certain limit, and will then be spilled over to disk.

     See libsvn_ra_serf/util.c  */
  struct svn_ra_serf__pending_t *pending;
};


/* v2 of the XML parsing functions  */

/* The XML parsing context.  */
typedef struct svn_ra_serf__xml_context_t svn_ra_serf__xml_context_t;


/* An opaque structure for the XML parse element/state.  */
typedef struct svn_ra_serf__xml_estate_t svn_ra_serf__xml_estate_t;

/* Called just after the parser moves into ENTERED_STATE. The tag causing
   the transition is passed in TAG.

   This callback is applied to a parsing context by using the
   svn_ra_serf__xml_context_customize() function.

   NOTE: this callback, when set, will be invoked on *every* transition.
   The callback must examine ENTERED_STATE to determine if any action
   must be taken. The original state is not provided, but must be derived
   from ENTERED_STATE and/or the TAG causing the transition (if needed).  */
typedef svn_error_t *
(*svn_ra_serf__xml_opened_t)(svn_ra_serf__xml_estate_t *xes,
                             void *baton,
                             int entered_state,
                             const svn_ra_serf__dav_props_t *tag,
                             apr_pool_t *scratch_pool);


/* Called just before the parser leaves LEAVING_STATE.

   If cdata collection was enabled for this state, then CDATA will be
   non-NULL and contain the collected cdata.

   If attribute collection was enabled for this state, then ATTRS will
   contain the attributes collected for this element only, along with
   any values stored via svn_ra_serf__xml_note().

   Use svn_ra_serf__xml_gather_since() to gather up data from outer states.

   ATTRS is char* -> char*.

   Temporary allocations may be made in SCRATCH_POOL.  */
typedef svn_error_t *
(*svn_ra_serf__xml_closed_t)(svn_ra_serf__xml_estate_t *xes,
                             void *baton,
                             int leaving_state,
                             const svn_string_t *cdata,
                             apr_hash_t *attrs,
                             apr_pool_t *scratch_pool);


/* Called for all states that are not using the builtin cdata collection.
   This callback is (only) appropriate for unbounded-size cdata content.

   CURRENT_STATE may be used to decide what to do with the data.

   Temporary allocations may be made in SCRATCH_POOL.  */
typedef svn_error_t *
(*svn_ra_serf__xml_cdata_t)(svn_ra_serf__xml_estate_t *xes,
                            void *baton,
                            int current_state,
                            const char *data,
                            apr_size_t len,
                            apr_pool_t *scratch_pool);


/* State transition table.

   When the XML Context is constructed, it is in state 0. User states are
   positive integers.

   In a list of transitions, use { 0 } to indicate the end. Specifically,
   the code looks for NS == NULL.

   ### more docco
*/
typedef struct svn_ra_serf__xml_transition_t {
  /* This transition applies when in this state  */
  int from_state;

  /* And when this tag is observed  */
  const char *ns;
  const char *name;

  /* Moving to this state  */
  int to_state;

  /* Should the cdata of NAME be collected? Note that CUSTOM_CLOSE should
     be TRUE in order to capture this cdata.  */
  svn_boolean_t collect_cdata;

  /* Which attributes of NAME should be collected? Terminate with NULL.
     Maximum of 10 attributes may be collected. Note that attribute
     namespaces are ignored at this time.

     Attribute names beginning with "?" are optional. Other names must
     exist on the element, or SVN_ERR_XML_ATTRIB_NOT_FOUND will be raised.  */
  const char *collect_attrs[11];

  /* When NAME is closed, should the callback be invoked?  */
  svn_boolean_t custom_close;

} svn_ra_serf__xml_transition_t;


/* Construct an XML parsing context, based on the TTABLE transition table.
   As content is parsed, the CLOSED_CB callback will be invoked according
   to the definition in the table.

   If OPENED_CB is not NULL, then it will be invoked for *every* tag-open
   event. The callback will need to use the ENTERED_STATE and TAG parameters
   to decide what it would like to do.

   If CDATA_CB is not NULL, then it will be called for all cdata that is
   not be automatically collected (based on the transition table record's
   COLLECT_CDATA flag). It will be called in every state, so the callback
   must examine the CURRENT_STATE parameter to decide what to do.

   The same BATON value will be passed to all three callbacks.

   The context will be created within RESULT_POOL.  */
svn_ra_serf__xml_context_t *
svn_ra_serf__xml_context_create(
  const svn_ra_serf__xml_transition_t *ttable,
  svn_ra_serf__xml_opened_t opened_cb,
  svn_ra_serf__xml_closed_t closed_cb,
  svn_ra_serf__xml_cdata_t cdata_cb,
  void *baton,
  apr_pool_t *result_pool);

/* Destroy all subpools for this structure. */
void
svn_ra_serf__xml_context_destroy(
  svn_ra_serf__xml_context_t *xmlctx);

/* Construct a handler with the response function/baton set up to parse
   a response body using the given XML context. The handler and its
   internal structures are allocated in RESULT_POOL.

   This also initializes HANDLER_POOL to the given RESULT_POOL.  */
svn_ra_serf__handler_t *
svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
                                  apr_pool_t *result_pool);


/* Allocated within XES->STATE_POOL. Changes are not allowd (callers
   should make a deep copy if they need to make changes).

   The resulting hash maps char* names to char* values.  */
apr_hash_t *
svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
                              int stop_state);


/* Attach the NAME/VALUE pair onto this/parent state identified by STATE.
   The name and value will be copied into the target state's pool.

   These values will be available to the CLOSED_CB for the target state,
   or part of the gathered state via xml_gather_since().

   Typically, this function is used by a child state's close callback,
   or within an opening callback to store additional data.

   Note: if the state is not found, then a programmer error has occurred,
   so the function will invoke SVN_ERR_MALFUNCTION().  */
void
svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
                      int state,
                      const char *name,
                      const char *value);


/* Returns XES->STATE_POOL for allocating structures that should live
   as long as the state identified by XES.

   Note: a state pool is created upon demand, so only use this function
   when memory is required for a given state.  */
apr_pool_t *
svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes);


/* Any XML parser may be used. When an opening tag is seen, call this
   function to feed the information into XMLCTX.  */
svn_error_t *
svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
                          const char *raw_name,
                          const char *const *attrs);


/* When a close tag is seen, call this function to feed the information
   into XMLCTX.  */
svn_error_t *
svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
                        const char *raw_name);


/* When cdata is parsed by the wrapping XML parser, call this function to
   feed the cdata into the XMLCTX.  */
svn_error_t *
svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
                          const char *data,
                          apr_size_t len);


/*
 * Parses a server-side error message into a local Subversion error.
 */
struct svn_ra_serf__server_error_t {
  /* Our local representation of the error. */
  svn_error_t *error;

  /* Are we done with the response? */
  svn_boolean_t done;

  /* Have we seen an error tag? */
  svn_boolean_t in_error;

  /* Have we seen a HTTP "412 Precondition Failed" error? */
  svn_boolean_t contains_precondition_error;

  /* Should we be collecting the XML cdata? */
  svn_boolean_t collect_cdata;

  /* Collected cdata. NULL if cdata not needed. */
  svn_stringbuf_t *cdata;

  /* XML parser and namespace used to parse the remote response */
  svn_ra_serf__xml_parser_t parser;
};


/*
 * Handler that discards the entire @a response body associated with a
 * @a request.  Implements svn_ra_serf__response_handler_t.
 *
 * If @a baton is a svn_ra_serf__server_error_t (i.e. non-NULL) and an
 * error is detected, it will be populated for later detection.
 *
 * All temporary allocations will be made in a @a pool.
 */
svn_error_t *
svn_ra_serf__handle_discard_body(serf_request_t *request,
                                 serf_bucket_t *response,
                                 void *baton,
                                 apr_pool_t *pool);


/*
 * Handler that retrieves the embedded XML multistatus response from the
 * the @a RESPONSE body associated with a @a REQUEST.
 *
 * Implements svn_ra_serf__response_handler_t.
 *
 * The @a BATON should be of type svn_ra_serf__handler_t. When the request
 * is complete, the handler's DONE flag will be set to TRUE.
 *
 * All temporary allocations will be made in a @a scratch_pool.
 */
svn_error_t *
svn_ra_serf__handle_multistatus_only(serf_request_t *request,
                                     serf_bucket_t *response,
                                     void *baton,
                                     apr_pool_t *scratch_pool);


/* Handler that expects an empty body.

   If a body IS present, and it is text/xml, then it will be parsed for
   a server-side error.

   BATON should be the svn_ra_serf__handler_t running REQUEST.

   Status line information will be in HANDLER->SLINE.

   Any parsed errors will be left in HANDLER->SERVER_ERROR. That member
   may be NULL if no body was present, or a problem occurred trying to
   parse the body.

   All temporary allocations will be made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__expect_empty_body(serf_request_t *request,
                               serf_bucket_t *response,
                               void *baton,
                               apr_pool_t *scratch_pool);


/*
 * This function will feed the RESPONSE body into XMLP.  When parsing is
 * completed (i.e. an EOF is received), *DONE is set to TRUE.
 * Implements svn_ra_serf__response_handler_t.
 *
 * If an error occurs during processing RESP_ERR is invoked with the
 * RESP_ERR_BATON.
 *
 * Temporary allocations are made in POOL.
 */
svn_error_t *
svn_ra_serf__handle_xml_parser(serf_request_t *request,
                               serf_bucket_t *response,
                               void *handler_baton,
                               apr_pool_t *pool);

/* serf_response_handler_t implementation that completely discards
 * the response.
 *
 * All temporary allocations will be made in @a pool.
 */
apr_status_t
svn_ra_serf__response_discard_handler(serf_request_t *request,
                                      serf_bucket_t *response,
                                      void *baton,
                                      apr_pool_t *pool);


/** XML helper functions. **/

/*
 * Advance the internal XML @a parser to the @a state.
 */
void
svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser,
                            int state);

/*
 * Return to the previous internal XML @a parser state.
 */
void
svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser);


svn_error_t *
svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser,
                             svn_boolean_t *network_eof,
                             apr_pool_t *scratch_pool);


/*
 * Add the appropriate serf buckets to @a agg_bucket represented by
 * the XML * @a tag and @a value.
 *
 * The bucket will be allocated from @a bkt_alloc.
 */
void
svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket,
                             const char *tag,
                             const char *value,
                             serf_bucket_alloc_t *bkt_alloc);

/*
 * Add the appropriate serf buckets to AGG_BUCKET with standard XML header:
 *  <?xml version="1.0" encoding="utf-8"?>
 *
 * The bucket will be allocated from BKT_ALLOC.
 */
void
svn_ra_serf__add_xml_header_buckets(serf_bucket_t *agg_bucket,
                                    serf_bucket_alloc_t *bkt_alloc);

/*
 * Add the appropriate serf buckets to AGG_BUCKET representing the XML
 * open tag with name TAG.
 *
 * Take the tag's attributes from varargs, a NULL-terminated list of
 * alternating <tt>char *</tt> key and <tt>char *</tt> val.  Attribute
 * will be ignored if it's value is NULL.
 *
 * NOTE: Callers are responsible for XML-escaping attribute values as
 * necessary.
 *
 * The bucket will be allocated from BKT_ALLOC.
 */
void
svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
                                  serf_bucket_alloc_t *bkt_alloc,
                                  const char *tag,
                                  ...);

/*
 * Add the appropriate serf buckets to AGG_BUCKET representing xml tag close
 * with name TAG.
 *
 * The bucket will be allocated from BKT_ALLOC.
 */
void
svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
                                   serf_bucket_alloc_t *bkt_alloc,
                                   const char *tag);

/*
 * Add the appropriate serf buckets to AGG_BUCKET with xml-escaped
 * version of DATA.
 *
 * The bucket will be allocated from BKT_ALLOC.
 */
void
svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket,
                                   serf_bucket_alloc_t *bkt_alloc,
                                   const char *data, apr_size_t len);
/*
 * Look up the @a attrs array for namespace definitions and add each one
 * to the @a ns_list of namespaces.
 *
 * New namespaces will be allocated in RESULT_POOL.
 */
void
svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
                       const char *const *attrs,
                       apr_pool_t *result_pool);

/*
 * Look up @a name in the @a ns_list list for previously declared namespace
 * definitions.
 *
 * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple
 * representing the expanded name.
 */
void
svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
                       const svn_ra_serf__ns_t *ns_list,
                       const char *name);


/** PROPFIND-related functions **/

/*
 * This function will deliver a PROP_CTX PROPFIND request in the SESS
 * serf context for the properties listed in LOOKUP_PROPS at URL for
 * DEPTH ("0","1","infinity").
 *
 * This function will not block waiting for the response. Callers are
 * expected to call svn_ra_serf__wait_for_props().
 */
svn_error_t *
svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
                           apr_hash_t *prop_vals,
                           svn_ra_serf__session_t *sess,
                           svn_ra_serf__connection_t *conn,
                           const char *url,
                           svn_revnum_t rev,
                           const char *depth,
                           const svn_ra_serf__dav_props_t *lookup_props,
                           svn_ra_serf__list_t **done_list,
                           apr_pool_t *pool);

/*
 * This helper function will block until PROPFIND_HANDLER indicates that is
 * done or another error is returned.
 */
svn_error_t *
svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
                            apr_pool_t *scratch_pool);

/* This is a blocking version of deliver_props.

   The properties are fetched and placed into RESULTS, allocated in
   RESULT_POOL.

   ### more docco about the other params.

   Temporary allocations are made in SCRATCH_POOL.
*/
svn_error_t *
svn_ra_serf__retrieve_props(apr_hash_t **results,
                            svn_ra_serf__session_t *sess,
                            svn_ra_serf__connection_t *conn,
                            const char *url,
                            svn_revnum_t rev,
                            const char *depth,
                            const svn_ra_serf__dav_props_t *props,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);


/* Using CONN, fetch the properties specified by WHICH_PROPS using CONN
   for URL at REVISION. The resulting properties are placed into a 2-level
   hash in RESULTS, mapping NAMESPACE -> hash<PROPNAME, PROPVALUE>, which
   is allocated in RESULT_POOL.

   If REVISION is SVN_INVALID_REVNUM, then the properties are fetched
   from HEAD for URL.

   This function performs the request synchronously.

   Temporary allocations are made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__fetch_node_props(apr_hash_t **results,
                              svn_ra_serf__connection_t *conn,
                              const char *url,
                              svn_revnum_t revision,
                              const svn_ra_serf__dav_props_t *which_props,
                              apr_pool_t *result_pool,
                              apr_pool_t *scratch_pool);


/* Using CONN, fetch a DAV: property from the resource identified by URL
   within REVISION. The PROPNAME may be one of:

     "checked-in"
     "href"

   The resulting value will be allocated in RESULT_POOL, and may be NULL
   if the property does not exist (note: "href" always exists).

   This function performs the request synchronously.

   Temporary allocations are made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__fetch_dav_prop(const char **value,
                            svn_ra_serf__connection_t *conn,
                            const char *url,
                            svn_revnum_t revision,
                            const char *propname,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);


/* Set PROPS for PATH at REV revision with a NS:NAME VAL.
 *
 * The POOL governs allocation.
 */
void
svn_ra_serf__set_ver_prop(apr_hash_t *props,
                          const char *path, svn_revnum_t rev,
                          const char *ns, const char *name,
                          const svn_string_t *val, apr_pool_t *pool);
#define svn_ra_serf__set_rev_prop svn_ra_serf__set_ver_prop

/** Property walker functions **/

typedef svn_error_t *
(*svn_ra_serf__walker_visitor_t)(void *baton,
                                 const char *ns,
                                 const char *name,
                                 const svn_string_t *val,
                                 apr_pool_t *pool);

svn_error_t *
svn_ra_serf__walk_all_props(apr_hash_t *props,
                            const char *name,
                            svn_revnum_t rev,
                            svn_ra_serf__walker_visitor_t walker,
                            void *baton,
                            apr_pool_t *pool);


/* Like walk_all_props(), but a 2-level hash.  */
svn_error_t *
svn_ra_serf__walk_node_props(apr_hash_t *props,
                             svn_ra_serf__walker_visitor_t walker,
                             void *baton,
                             apr_pool_t *scratch_pool);


typedef svn_error_t *
(*svn_ra_serf__path_rev_walker_t)(void *baton,
                                  const char *path, apr_ssize_t path_len,
                                  const char *ns, apr_ssize_t ns_len,
                                  const char *name, apr_ssize_t name_len,
                                  const svn_string_t *val,
                                  apr_pool_t *pool);
svn_error_t *
svn_ra_serf__walk_all_paths(apr_hash_t *props,
                            svn_revnum_t rev,
                            svn_ra_serf__path_rev_walker_t walker,
                            void *baton,
                            apr_pool_t *pool);


/* Map a property name, as passed over the wire, into its corresponding
   Subversion-internal name. The returned name will be a static value,
   or allocated within RESULT_POOL.

   If the property should be ignored (eg. some DAV properties), then NULL
   will be returned.  */
const char *
svn_ra_serf__svnname_from_wirename(const char *ns,
                                   const char *name,
                                   apr_pool_t *result_pool);


/* Select the basic revision properties from the set of "all" properties.
   Return these in *REVPROPS, allocated from RESULT_POOL.  */
svn_error_t *
svn_ra_serf__select_revprops(apr_hash_t **revprops,
                             const char *name,
                             svn_revnum_t rev,
                             apr_hash_t *all_revprops,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool);


/* PROPS is nested hash tables mapping NS -> NAME -> VALUE.
   This function takes the NS:NAME:VALUE hashes and flattens them into a set of
   names to VALUE. The names are composed of NS:NAME, with specific
   rewrite from wire names (DAV) to SVN names. This mapping is managed
   by the svn_ra_serf__set_baton_props() function.

   FLAT_PROPS is allocated in RESULT_POOL.
   ### right now, we do a shallow copy from PROPS to FLAT_PROPS. therefore,
   ### the names and values in PROPS must be in the proper pool.

   Temporary allocations are made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__flatten_props(apr_hash_t **flat_props,
                           apr_hash_t *props,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);


/* Return the property value for PATH at REV revision with a NS:NAME.
 * PROPS is a four-level nested hash: (svn_revnum_t => char *path =>
 * char *ns => char *name => svn_string_t *). */
const svn_string_t *
svn_ra_serf__get_ver_prop_string(apr_hash_t *props,
                                 const char *path, svn_revnum_t rev,
                                 const char *ns, const char *name);

/* Same as svn_ra_serf__get_ver_prop_string(), but returns a C string. */
const char *
svn_ra_serf__get_ver_prop(apr_hash_t *props,
                          const char *path, svn_revnum_t rev,
                          const char *ns, const char *name);

/* Same as svn_ra_serf__get_ver_prop_string(), but for the unknown revision. */
const svn_string_t *
svn_ra_serf__get_prop_string(apr_hash_t *props,
                             const char *path,
                             const char *ns,
                             const char *name);

/* Same as svn_ra_serf__get_ver_prop(), but for the unknown revision. */
const char *
svn_ra_serf__get_prop(apr_hash_t *props,
                      const char *path,
                      const char *ns,
                      const char *name);

/* Same as svn_ra_serf__set_rev_prop(), but for the unknown revision. */
void
svn_ra_serf__set_prop(apr_hash_t *props, const char *path,
                      const char *ns, const char *name,
                      const svn_string_t *val, apr_pool_t *pool);

svn_error_t *
svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
                               apr_hash_t *props);


/** MERGE-related functions **/

void
svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
                                   const char *parent,
                                   serf_bucket_t *body,
                                   serf_bucket_alloc_t *alloc,
                                   apr_pool_t *pool);

/* Create an MERGE request aimed at the SESSION url, requesting the
   merge of the resource identified by MERGE_RESOURCE_URL.
   LOCK_TOKENS is a hash mapping paths to lock tokens owned by the
   client.  If KEEP_LOCKS is set, instruct the server to not release
   locks set on the paths included in this commit.  */
svn_error_t *
svn_ra_serf__run_merge(const svn_commit_info_t **commit_info,
                       int *response_code,
                       svn_ra_serf__session_t *session,
                       svn_ra_serf__connection_t *conn,
                       const char *merge_resource_url,
                       apr_hash_t *lock_tokens,
                       svn_boolean_t keep_locks,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);


/** OPTIONS-related functions **/

/* When running with a proxy, we may need to detect and correct for problems.
   This probing function will send a simple OPTIONS request to detect problems
   with the connection.  */
svn_error_t *
svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
                         apr_pool_t *scratch_pool);


/* On HTTPv2 connections, run an OPTIONS request over CONN to fetch the
   current youngest revnum, returning it in *YOUNGEST.

   (the revnum is headers of the OPTIONS response)

   This function performs the request synchronously.

   All temporary allocations will be made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
                                    svn_ra_serf__connection_t *conn,
                                    apr_pool_t *scratch_pool);


/* On HTTPv1 connections, run an OPTIONS request over CONN to fetch the
   activity collection set and return it in *ACTIVITY_URL, allocated
   from RESULT_POOL.

   (the activity-collection-set is in the body of the OPTIONS response)

   This function performs the request synchronously.

   All temporary allocations will be made in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__v1_get_activity_collection(const char **activity_url,
                                        svn_ra_serf__connection_t *conn,
                                        apr_pool_t *result_pool,
                                        apr_pool_t *scratch_pool);


/* Set @a VCC_URL to the default VCC for our repository based on @a
 * ORIG_PATH for the session @a SESSION, ensuring that the VCC URL and
 * repository root URLs are cached in @a SESSION.  Use @a CONN for any
 * required network communications if it is non-NULL; otherwise use the
 * default connection.
 *
 * All temporary allocations will be made in @a POOL. */
svn_error_t *
svn_ra_serf__discover_vcc(const char **vcc_url,
                          svn_ra_serf__session_t *session,
                          svn_ra_serf__connection_t *conn,
                          apr_pool_t *pool);

/* Set @a REPORT_TARGET to the URI of the resource at which generic
 * (path-agnostic) REPORTs should be aimed for @a SESSION.  Use @a
 * CONN for any required network communications if it is non-NULL;
 * otherwise use the default connection.
 *
 * All temporary allocations will be made in @a POOL.
 */
svn_error_t *
svn_ra_serf__report_resource(const char **report_target,
                             svn_ra_serf__session_t *session,
                             svn_ra_serf__connection_t *conn,
                             apr_pool_t *pool);

/* Set @a REL_PATH to a path (not URI-encoded) relative to the root of
 * the repository pointed to by @a SESSION, based on original path
 * (URI-encoded) @a ORIG_PATH.  Use @a CONN for any required network
 * communications if it is non-NULL; otherwise use the default
 * connection.  Use POOL for allocations.  */
svn_error_t *
svn_ra_serf__get_relative_path(const char **rel_path,
                               const char *orig_path,
                               svn_ra_serf__session_t *session,
                               svn_ra_serf__connection_t *conn,
                               apr_pool_t *pool);


/* Using the default connection in SESSION (conns[0]), get the youngest
   revnum from the server, returning it in *YOUNGEST.

   This function operates synchronously.

   All temporary allocations are performed in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
                                 svn_ra_serf__session_t *session,
                                 apr_pool_t *scratch_pool);


/* Generate a revision-stable URL.

   The RA APIs all refer to user/public URLs that float along with the
   youngest revision. In many cases, we do NOT want to work with that URL
   since it can change from one moment to the next. Especially if we
   attempt to operation against multiple floating URLs -- we could end up
   referring to two separate revisions.

   The DAV RA provider(s) solve this by generating a URL that is specific
   to a revision by using a URL into a "baseline collection".

   For a specified SESSION, with an optional CONN (if NULL, then the
   session's default connection will be used; specifically SESSION->conns[0]),
   generate a revision-stable URL for URL at REVISION. If REVISION is
   SVN_INVALID_REVNUM, then the stable URL will refer to the youngest
   revision at the time this function was called.

   If URL is NULL, then the session root will be used.

   The stable URL will be placed into *STABLE_URL, allocated from RESULT_POOL.

   If LATEST_REVNUM is not NULL, then the revision used will be placed into
   *LATEST_REVNUM. That will be equal to youngest, or the given REVISION.

   This function operates synchronously, if any communication to the server
   is required. Communication is needed if REVISION is SVN_INVALID_REVNUM
   (to get the current youngest revnum), or if the specified REVISION is not
   (yet) in our cache of baseline collections.

   All temporary allocations are performed in SCRATCH_POOL.  */
svn_error_t *
svn_ra_serf__get_stable_url(const char **stable_url,
                            svn_revnum_t *latest_revnum,
                            svn_ra_serf__session_t *session,
                            svn_ra_serf__connection_t *conn,
                            const char *url,
                            svn_revnum_t revision,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);


/** RA functions **/

/* Implements svn_ra__vtable_t.get_log(). */
svn_error_t *
svn_ra_serf__get_log(svn_ra_session_t *session,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
                     svn_revnum_t end,
                     int limit,
                     svn_boolean_t discover_changed_paths,
                     svn_boolean_t strict_node_history,
                     svn_boolean_t include_merged_revisions,
                     const apr_array_header_t *revprops,
                     svn_log_entry_receiver_t receiver,
                     void *receiver_baton,
                     apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_locations(). */
svn_error_t *
svn_ra_serf__get_locations(svn_ra_session_t *session,
                           apr_hash_t **locations,
                           const char *path,
                           svn_revnum_t peg_revision,
                           const apr_array_header_t *location_revisions,
                           apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_location_segments(). */
svn_error_t *
svn_ra_serf__get_location_segments(svn_ra_session_t *session,
                                   const char *path,
                                   svn_revnum_t peg_revision,
                                   svn_revnum_t start_rev,
                                   svn_revnum_t end_rev,
                                   svn_location_segment_receiver_t receiver,
                                   void *receiver_baton,
                                   apr_pool_t *pool);

/* Implements svn_ra__vtable_t.do_diff(). */
svn_error_t *
svn_ra_serf__do_diff(svn_ra_session_t *session,
                     const svn_ra_reporter3_t **reporter,
                     void **report_baton,
                     svn_revnum_t revision,
                     const char *diff_target,
                     svn_depth_t depth,
                     svn_boolean_t ignore_ancestry,
                     svn_boolean_t text_deltas,
                     const char *versus_url,
                     const svn_delta_editor_t *diff_editor,
                     void *diff_baton,
                     apr_pool_t *pool);

/* Implements svn_ra__vtable_t.do_status(). */
svn_error_t *
svn_ra_serf__do_status(svn_ra_session_t *ra_session,
                       const svn_ra_reporter3_t **reporter,
                       void **report_baton,
                       const char *status_target,
                       svn_revnum_t revision,
                       svn_depth_t depth,
                       const svn_delta_editor_t *status_editor,
                       void *status_baton,
                       apr_pool_t *pool);

/* Implements svn_ra__vtable_t.do_update(). */
svn_error_t *
svn_ra_serf__do_update(svn_ra_session_t *ra_session,
                       const svn_ra_reporter3_t **reporter,
                       void **report_baton,
                       svn_revnum_t revision_to_update_to,
                       const char *update_target,
                       svn_depth_t depth,
                       svn_boolean_t send_copyfrom_args,
                       svn_boolean_t ignore_ancestry,
                       const svn_delta_editor_t *update_editor,
                       void *update_baton,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);

/* Implements svn_ra__vtable_t.do_switch(). */
svn_error_t *
svn_ra_serf__do_switch(svn_ra_session_t *ra_session,
                       const svn_ra_reporter3_t **reporter,
                       void **report_baton,
                       svn_revnum_t revision_to_switch_to,
                       const char *switch_target,
                       svn_depth_t depth,
                       const char *switch_url,
                       svn_boolean_t send_copyfrom_args,
                       svn_boolean_t ignore_ancestry,
                       const svn_delta_editor_t *switch_editor,
                       void *switch_baton,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);

/* Implements svn_ra__vtable_t.get_file_revs(). */
svn_error_t *
svn_ra_serf__get_file_revs(svn_ra_session_t *session,
                           const char *path,
                           svn_revnum_t start,
                           svn_revnum_t end,
                           svn_boolean_t include_merged_revisions,
                           svn_file_rev_handler_t handler,
                           void *handler_baton,
                           apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_dated_revision(). */
svn_error_t *
svn_ra_serf__get_dated_revision(svn_ra_session_t *session,
                                svn_revnum_t *revision,
                                apr_time_t tm,
                                apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_commit_editor(). */
svn_error_t *
svn_ra_serf__get_commit_editor(svn_ra_session_t *session,
                               const svn_delta_editor_t **editor,
                               void **edit_baton,
                               apr_hash_t *revprop_table,
                               svn_commit_callback2_t callback,
                               void *callback_baton,
                               apr_hash_t *lock_tokens,
                               svn_boolean_t keep_locks,
                               apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_file(). */
svn_error_t *
svn_ra_serf__get_file(svn_ra_session_t *session,
                      const char *path,
                      svn_revnum_t revision,
                      svn_stream_t *stream,
                      svn_revnum_t *fetched_rev,
                      apr_hash_t **props,
                      apr_pool_t *pool);

/* Implements svn_ra__vtable_t.change_rev_prop(). */
svn_error_t *
svn_ra_serf__change_rev_prop(svn_ra_session_t *session,
                             svn_revnum_t rev,
                             const char *name,
                             const svn_string_t *const *old_value_p,
                             const svn_string_t *value,
                             apr_pool_t *pool);

/* Implements svn_ra__vtable_t.replay(). */
svn_error_t *
svn_ra_serf__replay(svn_ra_session_t *ra_session,
                    svn_revnum_t revision,
                    svn_revnum_t low_water_mark,
                    svn_boolean_t text_deltas,
                    const svn_delta_editor_t *editor,
                    void *edit_baton,
                    apr_pool_t *pool);

/* Implements svn_ra__vtable_t.replay_range(). */
svn_error_t *
svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
                          svn_revnum_t start_revision,
                          svn_revnum_t end_revision,
                          svn_revnum_t low_water_mark,
                          svn_boolean_t send_deltas,
                          svn_ra_replay_revstart_callback_t revstart_func,
                          svn_ra_replay_revfinish_callback_t revfinish_func,
                          void *replay_baton,
                          apr_pool_t *pool);

/* Implements svn_ra__vtable_t.lock(). */
svn_error_t *
svn_ra_serf__lock(svn_ra_session_t *ra_session,
                  apr_hash_t *path_revs,
                  const char *comment,
                  svn_boolean_t force,
                  svn_ra_lock_callback_t lock_func,
                  void *lock_baton,
                  apr_pool_t *pool);

/* Implements svn_ra__vtable_t.unlock(). */
svn_error_t *
svn_ra_serf__unlock(svn_ra_session_t *ra_session,
                    apr_hash_t *path_tokens,
                    svn_boolean_t force,
                    svn_ra_lock_callback_t lock_func,
                    void *lock_baton,
                    apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_lock(). */
svn_error_t *
svn_ra_serf__get_lock(svn_ra_session_t *ra_session,
                      svn_lock_t **lock,
                      const char *path,
                      apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_locks(). */
svn_error_t *
svn_ra_serf__get_locks(svn_ra_session_t *ra_session,
                       apr_hash_t **locks,
                       const char *path,
                       svn_depth_t depth,
                       apr_pool_t *pool);

/* Request a mergeinfo-report from the URL attached to SESSION,
   and fill in the MERGEINFO hash with the results.

   Implements svn_ra__vtable_t.get_mergeinfo().
 */
svn_error_t *
svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
                           apr_hash_t **mergeinfo,
                           const apr_array_header_t *paths,
                           svn_revnum_t revision,
                           svn_mergeinfo_inheritance_t inherit,
                           svn_boolean_t include_descendants,
                           apr_pool_t *pool);

/* Exchange capabilities with the server, by sending an OPTIONS
 * request announcing the client's capabilities, and by filling
 * SERF_SESS->capabilities with the server's capabilities as read from
 * the response headers.  Use POOL only for temporary allocation.
 *
 * If the CORRECTED_URL is non-NULL, allow the OPTIONS response to
 * report a server-dictated redirect or relocation (HTTP 301 or 302
 * error codes), setting *CORRECTED_URL to the value of the corrected
 * repository URL.  Otherwise, such responses from the server will
 * generate an error.  (In either case, no capabilities are exchanged
 * if there is, in fact, such a response from the server.)
 */
svn_error_t *
svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
                                   const char **corrected_url,
                                   apr_pool_t *pool);

/* Implements svn_ra__vtable_t.has_capability(). */
svn_error_t *
svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
                            svn_boolean_t *has,
                            const char *capability,
                            apr_pool_t *pool);

/* Implements svn_ra__vtable_t.get_deleted_rev(). */
svn_error_t *
svn_ra_serf__get_deleted_rev(svn_ra_session_t *session,
                             const char *path,
                             svn_revnum_t peg_revision,
                             svn_revnum_t end_revision,
                             svn_revnum_t *revision_deleted,
                             apr_pool_t *pool);

/* Implements the get_inherited_props RA layer function. */
svn_error_t * svn_ra_serf__get_inherited_props(svn_ra_session_t *session,
                                               apr_array_header_t **iprops,
                                               const char *path,
                                               svn_revnum_t revision,
                                               apr_pool_t *result_pool,
                                               apr_pool_t *scratch_pool);

/* Implements svn_ra__vtable_t.get_repos_root(). */
svn_error_t *
svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
                            const char **url,
                            apr_pool_t *pool);

/* Implements svn_ra__vtable_t.register_editor_shim_callbacks(). */
svn_error_t *
svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
                                    svn_delta_shim_callbacks_t *callbacks);

/*** Authentication handler declarations ***/

/**
 * Callback function that loads the credentials for Basic and Digest
 * authentications, both for server and proxy authentication.
 */
apr_status_t
svn_ra_serf__credentials_callback(char **username, char **password,
                                  serf_request_t *request, void *baton,
                                  int code, const char *authn_type,
                                  const char *realm,
                                  apr_pool_t *pool);


/*** General utility functions ***/

/**
 * Convert an HTTP STATUS_CODE resulting from a WebDAV request against
 * PATH to the relevant error code.  Use the response-supplied LOCATION
 * where it necessary.
 */
svn_error_t *
svn_ra_serf__error_on_status(serf_status_line sline,
                             const char *path,
                             const char *location);

/* ###? */
svn_error_t *
svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf,
                                serf_bucket_t *bkt,
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool);

/* ###? */
serf_bucket_t *
svn_ra_serf__create_sb_bucket(svn_spillbuf_t *spillbuf,
                              serf_bucket_alloc_t *allocator,
                              apr_pool_t *result_pool,
                              apr_pool_t *scratch_pool);

/** Wrap STATUS from an serf function. If STATUS is not serf error code,
  * this is equivalent to svn_error_wrap_apr().
 */
svn_error_t *
svn_ra_serf__wrap_err(apr_status_t status,
                      const char *fmt,
                      ...);


#if defined(SVN_DEBUG)
/* Wrapper macros to collect file and line information */
#define svn_ra_serf__wrap_err \
  (svn_error__locate(__FILE__,__LINE__), (svn_ra_serf__wrap_err))

#endif

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* SVN_LIBSVN_RA_SERF_RA_SERF_H */