~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Transport/ReceivePack.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.Collections.Generic;
 
46
using System.IO;
 
47
using System.Text;
 
48
using NGit;
 
49
using NGit.Errors;
 
50
using NGit.Revwalk;
 
51
using NGit.Storage.File;
 
52
using NGit.Transport;
 
53
using NGit.Util.IO;
 
54
using Sharpen;
 
55
 
 
56
namespace NGit.Transport
 
57
{
 
58
        /// <summary>Implements the server side of a push connection, receiving objects.</summary>
 
59
        /// <remarks>Implements the server side of a push connection, receiving objects.</remarks>
 
60
        public class ReceivePack
 
61
        {
 
62
                /// <summary>Database we write the stored objects into.</summary>
 
63
                /// <remarks>Database we write the stored objects into.</remarks>
 
64
                private readonly Repository db;
 
65
 
 
66
                /// <summary>
 
67
                /// Revision traversal support over
 
68
                /// <see cref="db">db</see>
 
69
                /// .
 
70
                /// </summary>
 
71
                private readonly RevWalk walk;
 
72
 
 
73
                /// <summary>
 
74
                /// Is the client connection a bi-directional socket or pipe?
 
75
                /// <p>
 
76
                /// If true, this class assumes it can perform multiple read and write cycles
 
77
                /// with the client over the input and output streams.
 
78
                /// </summary>
 
79
                /// <remarks>
 
80
                /// Is the client connection a bi-directional socket or pipe?
 
81
                /// <p>
 
82
                /// If true, this class assumes it can perform multiple read and write cycles
 
83
                /// with the client over the input and output streams. This matches the
 
84
                /// functionality available with a standard TCP/IP connection, or a local
 
85
                /// operating system or in-memory pipe.
 
86
                /// <p>
 
87
                /// If false, this class runs in a read everything then output results mode,
 
88
                /// making it suitable for single round-trip systems RPCs such as HTTP.
 
89
                /// </remarks>
 
90
                private bool biDirectionalPipe = true;
 
91
 
 
92
                /// <summary>Should an incoming transfer validate objects?</summary>
 
93
                private bool checkReceivedObjects;
 
94
 
 
95
                /// <summary>Should an incoming transfer permit create requests?</summary>
 
96
                private bool allowCreates;
 
97
 
 
98
                /// <summary>Should an incoming transfer permit delete requests?</summary>
 
99
                private bool allowDeletes;
 
100
 
 
101
                /// <summary>Should an incoming transfer permit non-fast-forward requests?</summary>
 
102
                private bool allowNonFastForwards;
 
103
 
 
104
                private bool allowOfsDelta;
 
105
 
 
106
                /// <summary>Identity to record action as within the reflog.</summary>
 
107
                /// <remarks>Identity to record action as within the reflog.</remarks>
 
108
                private PersonIdent refLogIdent;
 
109
 
 
110
                /// <summary>Filter used while advertising the refs to the client.</summary>
 
111
                /// <remarks>Filter used while advertising the refs to the client.</remarks>
 
112
                private RefFilter refFilter;
 
113
 
 
114
                /// <summary>Hook to validate the update commands before execution.</summary>
 
115
                /// <remarks>Hook to validate the update commands before execution.</remarks>
 
116
                private PreReceiveHook preReceive;
 
117
 
 
118
                /// <summary>Hook to report on the commands after execution.</summary>
 
119
                /// <remarks>Hook to report on the commands after execution.</remarks>
 
120
                private PostReceiveHook postReceive;
 
121
 
 
122
                /// <summary>Timeout in seconds to wait for client interaction.</summary>
 
123
                /// <remarks>Timeout in seconds to wait for client interaction.</remarks>
 
124
                private int timeout;
 
125
 
 
126
                /// <summary>
 
127
                /// Timer to manage
 
128
                /// <see cref="timeout">timeout</see>
 
129
                /// .
 
130
                /// </summary>
 
131
                private InterruptTimer timer;
 
132
 
 
133
                private TimeoutInputStream timeoutIn;
 
134
 
 
135
                private InputStream rawIn;
 
136
 
 
137
                private OutputStream rawOut;
 
138
 
 
139
                private OutputStream msgOut;
 
140
 
 
141
                private PacketLineIn pckIn;
 
142
 
 
143
                private PacketLineOut pckOut;
 
144
 
 
145
                private PackParser parser;
 
146
 
 
147
                /// <summary>The refs we advertised as existing at the start of the connection.</summary>
 
148
                /// <remarks>The refs we advertised as existing at the start of the connection.</remarks>
 
149
                private IDictionary<string, Ref> refs;
 
150
 
 
151
                /// <summary>All SHA-1s shown to the client, which can be possible edges.</summary>
 
152
                /// <remarks>All SHA-1s shown to the client, which can be possible edges.</remarks>
 
153
                private ICollection<ObjectId> advertisedHaves;
 
154
 
 
155
                /// <summary>Capabilities requested by the client.</summary>
 
156
                /// <remarks>Capabilities requested by the client.</remarks>
 
157
                private ICollection<string> enabledCapablities;
 
158
 
 
159
                /// <summary>Commands to execute, as received by the client.</summary>
 
160
                /// <remarks>Commands to execute, as received by the client.</remarks>
 
161
                private IList<ReceiveCommand> commands;
 
162
 
 
163
                /// <summary>Error to display instead of advertising the references.</summary>
 
164
                /// <remarks>Error to display instead of advertising the references.</remarks>
 
165
                private StringBuilder advertiseError;
 
166
 
 
167
                /// <summary>An exception caught while unpacking and fsck'ing the objects.</summary>
 
168
                /// <remarks>An exception caught while unpacking and fsck'ing the objects.</remarks>
 
169
                private Exception unpackError;
 
170
 
 
171
                /// <summary>
 
172
                /// If
 
173
                /// <see cref="BasePackPushConnection.CAPABILITY_REPORT_STATUS">BasePackPushConnection.CAPABILITY_REPORT_STATUS
 
174
                ///     </see>
 
175
                /// is enabled.
 
176
                /// </summary>
 
177
                private bool reportStatus;
 
178
 
 
179
                /// <summary>
 
180
                /// If
 
181
                /// <see cref="BasePackPushConnection.CAPABILITY_SIDE_BAND_64K">BasePackPushConnection.CAPABILITY_SIDE_BAND_64K
 
182
                ///     </see>
 
183
                /// is enabled.
 
184
                /// </summary>
 
185
                private bool sideBand;
 
186
 
 
187
                /// <summary>Lock around the received pack file, while updating refs.</summary>
 
188
                /// <remarks>Lock around the received pack file, while updating refs.</remarks>
 
189
                private PackLock packLock;
 
190
 
 
191
                private bool checkReferencedIsReachable;
 
192
 
 
193
                /// <summary>Create a new pack receive for an open repository.</summary>
 
194
                /// <remarks>Create a new pack receive for an open repository.</remarks>
 
195
                /// <param name="into">the destination repository.</param>
 
196
                public ReceivePack(Repository into)
 
197
                {
 
198
                        db = into;
 
199
                        walk = new RevWalk(db);
 
200
                        ReceivePack.ReceiveConfig cfg = db.GetConfig().Get(ReceivePack.ReceiveConfig.KEY);
 
201
                        checkReceivedObjects = cfg.checkReceivedObjects;
 
202
                        allowCreates = cfg.allowCreates;
 
203
                        allowDeletes = cfg.allowDeletes;
 
204
                        allowNonFastForwards = cfg.allowNonFastForwards;
 
205
                        allowOfsDelta = cfg.allowOfsDelta;
 
206
                        refFilter = RefFilter.DEFAULT;
 
207
                        preReceive = PreReceiveHook.NULL;
 
208
                        postReceive = PostReceiveHook.NULL;
 
209
                        advertisedHaves = new HashSet<ObjectId>();
 
210
                }
 
211
 
 
212
                private class ReceiveConfig
 
213
                {
 
214
                        private sealed class _SectionParser_218 : Config.SectionParser<ReceivePack.ReceiveConfig
 
215
                                >
 
216
                        {
 
217
                                public _SectionParser_218()
 
218
                                {
 
219
                                }
 
220
 
 
221
                                public ReceivePack.ReceiveConfig Parse(Config cfg)
 
222
                                {
 
223
                                        return new ReceivePack.ReceiveConfig(cfg);
 
224
                                }
 
225
                        }
 
226
 
 
227
                        internal static readonly Config.SectionParser<ReceivePack.ReceiveConfig> KEY = new 
 
228
                                _SectionParser_218();
 
229
 
 
230
                        internal readonly bool checkReceivedObjects;
 
231
 
 
232
                        internal readonly bool allowCreates;
 
233
 
 
234
                        internal readonly bool allowDeletes;
 
235
 
 
236
                        internal readonly bool allowNonFastForwards;
 
237
 
 
238
                        internal readonly bool allowOfsDelta;
 
239
 
 
240
                        internal ReceiveConfig(Config config)
 
241
                        {
 
242
                                checkReceivedObjects = config.GetBoolean("receive", "fsckobjects", false);
 
243
                                allowCreates = true;
 
244
                                allowDeletes = !config.GetBoolean("receive", "denydeletes", false);
 
245
                                allowNonFastForwards = !config.GetBoolean("receive", "denynonfastforwards", false
 
246
                                        );
 
247
                                allowOfsDelta = config.GetBoolean("repack", "usedeltabaseoffset", true);
 
248
                        }
 
249
                }
 
250
 
 
251
                /// <returns>the repository this receive completes into.</returns>
 
252
                public Repository GetRepository()
 
253
                {
 
254
                        return db;
 
255
                }
 
256
 
 
257
                /// <returns>the RevWalk instance used by this connection.</returns>
 
258
                public RevWalk GetRevWalk()
 
259
                {
 
260
                        return walk;
 
261
                }
 
262
 
 
263
                /// <returns>all refs which were advertised to the client.</returns>
 
264
                public IDictionary<string, Ref> GetAdvertisedRefs()
 
265
                {
 
266
                        if (refs == null)
 
267
                        {
 
268
                                refs = refFilter.Filter(db.GetAllRefs());
 
269
                                Ref head = refs.Get(Constants.HEAD);
 
270
                                if (head != null && head.IsSymbolic())
 
271
                                {
 
272
                                        Sharpen.Collections.Remove(refs, Constants.HEAD);
 
273
                                }
 
274
                                foreach (Ref @ref in refs.Values)
 
275
                                {
 
276
                                        if (@ref.GetObjectId() != null)
 
277
                                        {
 
278
                                                advertisedHaves.AddItem(@ref.GetObjectId());
 
279
                                        }
 
280
                                }
 
281
                                Sharpen.Collections.AddAll(advertisedHaves, db.GetAdditionalHaves());
 
282
                        }
 
283
                        return refs;
 
284
                }
 
285
 
 
286
                /// <returns>the set of objects advertised as present in this repository.</returns>
 
287
                public ICollection<ObjectId> GetAdvertisedObjects()
 
288
                {
 
289
                        GetAdvertisedRefs();
 
290
                        return advertisedHaves;
 
291
                }
 
292
 
 
293
                /// <returns>
 
294
                /// true if this instance will validate all referenced, but not
 
295
                /// supplied by the client, objects are reachable from another
 
296
                /// reference.
 
297
                /// </returns>
 
298
                public virtual bool IsCheckReferencedObjectsAreReachable()
 
299
                {
 
300
                        return checkReferencedIsReachable;
 
301
                }
 
302
 
 
303
                /// <summary>Validate all referenced but not supplied objects are reachable.</summary>
 
304
                /// <remarks>
 
305
                /// Validate all referenced but not supplied objects are reachable.
 
306
                /// <p>
 
307
                /// If enabled, this instance will verify that references to objects not
 
308
                /// contained within the received pack are already reachable through at least
 
309
                /// one other reference selected by the
 
310
                /// <see cref="GetRefFilter()">GetRefFilter()</see>
 
311
                /// and displayed
 
312
                /// as part of
 
313
                /// <see cref="GetAdvertisedRefs()">GetAdvertisedRefs()</see>
 
314
                /// .
 
315
                /// <p>
 
316
                /// This feature is useful when the application doesn't trust the client to
 
317
                /// not provide a forged SHA-1 reference to an object, in an attempt to
 
318
                /// access parts of the DAG that they aren't allowed to see and which have
 
319
                /// been hidden from them via the configured
 
320
                /// <see cref="RefFilter">RefFilter</see>
 
321
                /// .
 
322
                /// <p>
 
323
                /// Enabling this feature may imply at least some, if not all, of the same
 
324
                /// functionality performed by
 
325
                /// <see cref="SetCheckReceivedObjects(bool)">SetCheckReceivedObjects(bool)</see>
 
326
                /// .
 
327
                /// Applications are encouraged to enable both features, if desired.
 
328
                /// </remarks>
 
329
                /// <param name="b">
 
330
                /// <code>true</code>
 
331
                /// to enable the additional check.
 
332
                /// </param>
 
333
                public virtual void SetCheckReferencedObjectsAreReachable(bool b)
 
334
                {
 
335
                        this.checkReferencedIsReachable = b;
 
336
                }
 
337
 
 
338
                /// <returns>
 
339
                /// true if this class expects a bi-directional pipe opened between
 
340
                /// the client and itself. The default is true.
 
341
                /// </returns>
 
342
                public virtual bool IsBiDirectionalPipe()
 
343
                {
 
344
                        return biDirectionalPipe;
 
345
                }
 
346
 
 
347
                /// <param name="twoWay">
 
348
                /// if true, this class will assume the socket is a fully
 
349
                /// bidirectional pipe between the two peers and takes advantage
 
350
                /// of that by first transmitting the known refs, then waiting to
 
351
                /// read commands. If false, this class assumes it must read the
 
352
                /// commands before writing output and does not perform the
 
353
                /// initial advertising.
 
354
                /// </param>
 
355
                public virtual void SetBiDirectionalPipe(bool twoWay)
 
356
                {
 
357
                        biDirectionalPipe = twoWay;
 
358
                }
 
359
 
 
360
                /// <returns>
 
361
                /// true if this instance will verify received objects are formatted
 
362
                /// correctly. Validating objects requires more CPU time on this side
 
363
                /// of the connection.
 
364
                /// </returns>
 
365
                public virtual bool IsCheckReceivedObjects()
 
366
                {
 
367
                        return checkReceivedObjects;
 
368
                }
 
369
 
 
370
                /// <param name="check">
 
371
                /// true to enable checking received objects; false to assume all
 
372
                /// received objects are valid.
 
373
                /// </param>
 
374
                public virtual void SetCheckReceivedObjects(bool check)
 
375
                {
 
376
                        checkReceivedObjects = check;
 
377
                }
 
378
 
 
379
                /// <returns>true if the client can request refs to be created.</returns>
 
380
                public virtual bool IsAllowCreates()
 
381
                {
 
382
                        return allowCreates;
 
383
                }
 
384
 
 
385
                /// <param name="canCreate">true to permit create ref commands to be processed.</param>
 
386
                public virtual void SetAllowCreates(bool canCreate)
 
387
                {
 
388
                        allowCreates = canCreate;
 
389
                }
 
390
 
 
391
                /// <returns>true if the client can request refs to be deleted.</returns>
 
392
                public virtual bool IsAllowDeletes()
 
393
                {
 
394
                        return allowDeletes;
 
395
                }
 
396
 
 
397
                /// <param name="canDelete">true to permit delete ref commands to be processed.</param>
 
398
                public virtual void SetAllowDeletes(bool canDelete)
 
399
                {
 
400
                        allowDeletes = canDelete;
 
401
                }
 
402
 
 
403
                /// <returns>
 
404
                /// true if the client can request non-fast-forward updates of a ref,
 
405
                /// possibly making objects unreachable.
 
406
                /// </returns>
 
407
                public virtual bool IsAllowNonFastForwards()
 
408
                {
 
409
                        return allowNonFastForwards;
 
410
                }
 
411
 
 
412
                /// <param name="canRewind">
 
413
                /// true to permit the client to ask for non-fast-forward updates
 
414
                /// of an existing ref.
 
415
                /// </param>
 
416
                public virtual void SetAllowNonFastForwards(bool canRewind)
 
417
                {
 
418
                        allowNonFastForwards = canRewind;
 
419
                }
 
420
 
 
421
                /// <returns>identity of the user making the changes in the reflog.</returns>
 
422
                public virtual PersonIdent GetRefLogIdent()
 
423
                {
 
424
                        return refLogIdent;
 
425
                }
 
426
 
 
427
                /// <summary>Set the identity of the user appearing in the affected reflogs.</summary>
 
428
                /// <remarks>
 
429
                /// Set the identity of the user appearing in the affected reflogs.
 
430
                /// <p>
 
431
                /// The timestamp portion of the identity is ignored. A new identity with the
 
432
                /// current timestamp will be created automatically when the updates occur
 
433
                /// and the log records are written.
 
434
                /// </remarks>
 
435
                /// <param name="pi">
 
436
                /// identity of the user. If null the identity will be
 
437
                /// automatically determined based on the repository
 
438
                /// configuration.
 
439
                /// </param>
 
440
                public virtual void SetRefLogIdent(PersonIdent pi)
 
441
                {
 
442
                        refLogIdent = pi;
 
443
                }
 
444
 
 
445
                /// <returns>the filter used while advertising the refs to the client</returns>
 
446
                public virtual RefFilter GetRefFilter()
 
447
                {
 
448
                        return refFilter;
 
449
                }
 
450
 
 
451
                /// <summary>Set the filter used while advertising the refs to the client.</summary>
 
452
                /// <remarks>
 
453
                /// Set the filter used while advertising the refs to the client.
 
454
                /// <p>
 
455
                /// Only refs allowed by this filter will be shown to the client.
 
456
                /// Clients may still attempt to create or update a reference hidden
 
457
                /// by the configured
 
458
                /// <see cref="RefFilter">RefFilter</see>
 
459
                /// . These attempts should be
 
460
                /// rejected by a matching
 
461
                /// <see cref="PreReceiveHook">PreReceiveHook</see>
 
462
                /// .
 
463
                /// </remarks>
 
464
                /// <param name="refFilter">the filter; may be null to show all refs.</param>
 
465
                public virtual void SetRefFilter(RefFilter refFilter)
 
466
                {
 
467
                        this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
 
468
                }
 
469
 
 
470
                /// <returns>get the hook invoked before updates occur.</returns>
 
471
                public virtual PreReceiveHook GetPreReceiveHook()
 
472
                {
 
473
                        return preReceive;
 
474
                }
 
475
 
 
476
                /// <summary>Set the hook which is invoked prior to commands being executed.</summary>
 
477
                /// <remarks>
 
478
                /// Set the hook which is invoked prior to commands being executed.
 
479
                /// <p>
 
480
                /// Only valid commands (those which have no obvious errors according to the
 
481
                /// received input and this instance's configuration) are passed into the
 
482
                /// hook. The hook may mark a command with a result of any value other than
 
483
                /// <see cref="Result.NOT_ATTEMPTED">Result.NOT_ATTEMPTED</see>
 
484
                /// to block its execution.
 
485
                /// <p>
 
486
                /// The hook may be called with an empty command collection if the current
 
487
                /// set is completely invalid.
 
488
                /// </remarks>
 
489
                /// <param name="h">the hook instance; may be null to disable the hook.</param>
 
490
                public virtual void SetPreReceiveHook(PreReceiveHook h)
 
491
                {
 
492
                        preReceive = h != null ? h : PreReceiveHook.NULL;
 
493
                }
 
494
 
 
495
                /// <returns>get the hook invoked after updates occur.</returns>
 
496
                public virtual PostReceiveHook GetPostReceiveHook()
 
497
                {
 
498
                        return postReceive;
 
499
                }
 
500
 
 
501
                /// <summary>Set the hook which is invoked after commands are executed.</summary>
 
502
                /// <remarks>
 
503
                /// Set the hook which is invoked after commands are executed.
 
504
                /// <p>
 
505
                /// Only successful commands (type is
 
506
                /// <see cref="Result.OK">Result.OK</see>
 
507
                /// ) are passed into the
 
508
                /// hook. The hook may be called with an empty command collection if the
 
509
                /// current set all resulted in an error.
 
510
                /// </remarks>
 
511
                /// <param name="h">the hook instance; may be null to disable the hook.</param>
 
512
                public virtual void SetPostReceiveHook(PostReceiveHook h)
 
513
                {
 
514
                        postReceive = h != null ? h : PostReceiveHook.NULL;
 
515
                }
 
516
 
 
517
                /// <returns>timeout (in seconds) before aborting an IO operation.</returns>
 
518
                public virtual int GetTimeout()
 
519
                {
 
520
                        return timeout;
 
521
                }
 
522
 
 
523
                /// <summary>Set the timeout before willing to abort an IO call.</summary>
 
524
                /// <remarks>Set the timeout before willing to abort an IO call.</remarks>
 
525
                /// <param name="seconds">
 
526
                /// number of seconds to wait (with no data transfer occurring)
 
527
                /// before aborting an IO read or write operation with the
 
528
                /// connected client.
 
529
                /// </param>
 
530
                public virtual void SetTimeout(int seconds)
 
531
                {
 
532
                        timeout = seconds;
 
533
                }
 
534
 
 
535
                /// <returns>all of the command received by the current request.</returns>
 
536
                public virtual IList<ReceiveCommand> GetAllCommands()
 
537
                {
 
538
                        return Sharpen.Collections.UnmodifiableList(commands);
 
539
                }
 
540
 
 
541
                /// <summary>Send an error message to the client.</summary>
 
542
                /// <remarks>
 
543
                /// Send an error message to the client.
 
544
                /// <p>
 
545
                /// If any error messages are sent before the references are advertised to
 
546
                /// the client, the errors will be sent instead of the advertisement and the
 
547
                /// receive operation will be aborted. All clients should receive and display
 
548
                /// such early stage errors.
 
549
                /// <p>
 
550
                /// If the reference advertisements have already been sent, messages are sent
 
551
                /// in a side channel. If the client doesn't support receiving messages, the
 
552
                /// message will be discarded, with no other indication to the caller or to
 
553
                /// the client.
 
554
                /// <p>
 
555
                /// <see cref="PreReceiveHook">PreReceiveHook</see>
 
556
                /// s should always try to use
 
557
                /// <see cref="ReceiveCommand.SetResult(Result, string)">ReceiveCommand.SetResult(Result, string)
 
558
                ///     </see>
 
559
                /// with a result status of
 
560
                /// <see cref="Result.REJECTED_OTHER_REASON">Result.REJECTED_OTHER_REASON</see>
 
561
                /// to indicate any reasons for
 
562
                /// rejecting an update. Messages attached to a command are much more likely
 
563
                /// to be returned to the client.
 
564
                /// </remarks>
 
565
                /// <param name="what">
 
566
                /// string describing the problem identified by the hook. The
 
567
                /// string must not end with an LF, and must not contain an LF.
 
568
                /// </param>
 
569
                public virtual void SendError(string what)
 
570
                {
 
571
                        if (refs == null)
 
572
                        {
 
573
                                if (advertiseError == null)
 
574
                                {
 
575
                                        advertiseError = new StringBuilder();
 
576
                                }
 
577
                                advertiseError.Append(what).Append('\n');
 
578
                        }
 
579
                        else
 
580
                        {
 
581
                                try
 
582
                                {
 
583
                                        if (msgOut != null)
 
584
                                        {
 
585
                                                msgOut.Write(Constants.Encode("error: " + what + "\n"));
 
586
                                        }
 
587
                                }
 
588
                                catch (IOException)
 
589
                                {
 
590
                                }
 
591
                        }
 
592
                }
 
593
 
 
594
                // Ignore write failures.
 
595
                /// <summary>Send a message to the client, if it supports receiving them.</summary>
 
596
                /// <remarks>
 
597
                /// Send a message to the client, if it supports receiving them.
 
598
                /// <p>
 
599
                /// If the client doesn't support receiving messages, the message will be
 
600
                /// discarded, with no other indication to the caller or to the client.
 
601
                /// </remarks>
 
602
                /// <param name="what">
 
603
                /// string describing the problem identified by the hook. The
 
604
                /// string must not end with an LF, and must not contain an LF.
 
605
                /// </param>
 
606
                public virtual void SendMessage(string what)
 
607
                {
 
608
                        try
 
609
                        {
 
610
                                if (msgOut != null)
 
611
                                {
 
612
                                        msgOut.Write(Constants.Encode(what + "\n"));
 
613
                                }
 
614
                        }
 
615
                        catch (IOException)
 
616
                        {
 
617
                        }
 
618
                }
 
619
 
 
620
                // Ignore write failures.
 
621
                /// <summary>Execute the receive task on the socket.</summary>
 
622
                /// <remarks>Execute the receive task on the socket.</remarks>
 
623
                /// <param name="input">
 
624
                /// raw input to read client commands and pack data from. Caller
 
625
                /// must ensure the input is buffered, otherwise read performance
 
626
                /// may suffer.
 
627
                /// </param>
 
628
                /// <param name="output">
 
629
                /// response back to the Git network client. Caller must ensure
 
630
                /// the output is buffered, otherwise write performance may
 
631
                /// suffer.
 
632
                /// </param>
 
633
                /// <param name="messages">
 
634
                /// secondary "notice" channel to send additional messages out
 
635
                /// through. When run over SSH this should be tied back to the
 
636
                /// standard error channel of the command execution. For most
 
637
                /// other network connections this should be null.
 
638
                /// </param>
 
639
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
640
                public virtual void Receive(InputStream input, OutputStream output, OutputStream 
 
641
                        messages)
 
642
                {
 
643
                        try
 
644
                        {
 
645
                                rawIn = input;
 
646
                                rawOut = output;
 
647
                                msgOut = messages;
 
648
                                if (timeout > 0)
 
649
                                {
 
650
                                        Sharpen.Thread caller = Sharpen.Thread.CurrentThread();
 
651
                                        timer = new InterruptTimer(caller.GetName() + "-Timer");
 
652
                                        timeoutIn = new TimeoutInputStream(rawIn, timer);
 
653
                                        TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
 
654
                                        timeoutIn.SetTimeout(timeout * 1000);
 
655
                                        o.SetTimeout(timeout * 1000);
 
656
                                        rawIn = timeoutIn;
 
657
                                        rawOut = o;
 
658
                                }
 
659
                                pckIn = new PacketLineIn(rawIn);
 
660
                                pckOut = new PacketLineOut(rawOut);
 
661
                                pckOut.SetFlushOnEnd(false);
 
662
                                enabledCapablities = new HashSet<string>();
 
663
                                commands = new AList<ReceiveCommand>();
 
664
                                Service();
 
665
                        }
 
666
                        finally
 
667
                        {
 
668
                                walk.Release();
 
669
                                try
 
670
                                {
 
671
                                        if (sideBand)
 
672
                                        {
 
673
                                                // If we are using side band, we need to send a final
 
674
                                                // flush-pkt to tell the remote peer the side band is
 
675
                                                // complete and it should stop decoding. We need to
 
676
                                                // use the original output stream as rawOut is now the
 
677
                                                // side band data channel.
 
678
                                                //
 
679
                                                ((SideBandOutputStream)msgOut).FlushBuffer();
 
680
                                                ((SideBandOutputStream)rawOut).FlushBuffer();
 
681
                                                PacketLineOut plo = new PacketLineOut(output);
 
682
                                                plo.SetFlushOnEnd(false);
 
683
                                                plo.End();
 
684
                                        }
 
685
                                        if (biDirectionalPipe)
 
686
                                        {
 
687
                                                // If this was a native git connection, flush the pipe for
 
688
                                                // the caller. For smart HTTP we don't do this flush and
 
689
                                                // instead let the higher level HTTP servlet code do it.
 
690
                                                //
 
691
                                                if (!sideBand && msgOut != null)
 
692
                                                {
 
693
                                                        msgOut.Flush();
 
694
                                                }
 
695
                                                rawOut.Flush();
 
696
                                        }
 
697
                                }
 
698
                                finally
 
699
                                {
 
700
                                        UnlockPack();
 
701
                                        timeoutIn = null;
 
702
                                        rawIn = null;
 
703
                                        rawOut = null;
 
704
                                        msgOut = null;
 
705
                                        pckIn = null;
 
706
                                        pckOut = null;
 
707
                                        refs = null;
 
708
                                        enabledCapablities = null;
 
709
                                        commands = null;
 
710
                                        if (timer != null)
 
711
                                        {
 
712
                                                try
 
713
                                                {
 
714
                                                        timer.Terminate();
 
715
                                                }
 
716
                                                finally
 
717
                                                {
 
718
                                                        timer = null;
 
719
                                                }
 
720
                                        }
 
721
                                }
 
722
                        }
 
723
                }
 
724
 
 
725
                /// <exception cref="System.IO.IOException"></exception>
 
726
                private void Service()
 
727
                {
 
728
                        if (biDirectionalPipe)
 
729
                        {
 
730
                                SendAdvertisedRefs(new RefAdvertiser.PacketLineOutRefAdvertiser(pckOut));
 
731
                                pckOut.Flush();
 
732
                        }
 
733
                        else
 
734
                        {
 
735
                                GetAdvertisedRefs();
 
736
                        }
 
737
                        if (advertiseError != null)
 
738
                        {
 
739
                                return;
 
740
                        }
 
741
                        RecvCommands();
 
742
                        if (!commands.IsEmpty())
 
743
                        {
 
744
                                EnableCapabilities();
 
745
                                if (NeedPack())
 
746
                                {
 
747
                                        try
 
748
                                        {
 
749
                                                DoReceivePack();
 
750
                                                if (NeedCheckConnectivity())
 
751
                                                {
 
752
                                                        CheckConnectivity();
 
753
                                                }
 
754
                                                parser = null;
 
755
                                                unpackError = null;
 
756
                                        }
 
757
                                        catch (IOException err)
 
758
                                        {
 
759
                                                unpackError = err;
 
760
                                        }
 
761
                                        catch (RuntimeException err)
 
762
                                        {
 
763
                                                unpackError = err;
 
764
                                        }
 
765
                                        catch (Error err)
 
766
                                        {
 
767
                                                unpackError = err;
 
768
                                        }
 
769
                                }
 
770
                                if (unpackError == null)
 
771
                                {
 
772
                                        ValidateCommands();
 
773
                                        ExecuteCommands();
 
774
                                }
 
775
                                UnlockPack();
 
776
                                if (reportStatus)
 
777
                                {
 
778
                                        SendStatusReport(true, new _Reporter_685(this));
 
779
                                        pckOut.End();
 
780
                                }
 
781
                                else
 
782
                                {
 
783
                                        if (msgOut != null)
 
784
                                        {
 
785
                                                SendStatusReport(false, new _Reporter_692(this));
 
786
                                        }
 
787
                                }
 
788
                                postReceive.OnPostReceive(this, FilterCommands(ReceiveCommand.Result.OK));
 
789
                                if (unpackError != null)
 
790
                                {
 
791
                                        throw new UnpackException(unpackError);
 
792
                                }
 
793
                        }
 
794
                }
 
795
 
 
796
                private sealed class _Reporter_685 : ReceivePack.Reporter
 
797
                {
 
798
                        public _Reporter_685(ReceivePack _enclosing)
 
799
                        {
 
800
                                this._enclosing = _enclosing;
 
801
                        }
 
802
 
 
803
                        /// <exception cref="System.IO.IOException"></exception>
 
804
                        internal override void SendString(string s)
 
805
                        {
 
806
                                this._enclosing.pckOut.WriteString(s + "\n");
 
807
                        }
 
808
 
 
809
                        private readonly ReceivePack _enclosing;
 
810
                }
 
811
 
 
812
                private sealed class _Reporter_692 : ReceivePack.Reporter
 
813
                {
 
814
                        public _Reporter_692(ReceivePack _enclosing)
 
815
                        {
 
816
                                this._enclosing = _enclosing;
 
817
                        }
 
818
 
 
819
                        /// <exception cref="System.IO.IOException"></exception>
 
820
                        internal override void SendString(string s)
 
821
                        {
 
822
                                this._enclosing.msgOut.Write(Constants.Encode(s + "\n"));
 
823
                        }
 
824
 
 
825
                        private readonly ReceivePack _enclosing;
 
826
                }
 
827
 
 
828
                /// <exception cref="System.IO.IOException"></exception>
 
829
                private void UnlockPack()
 
830
                {
 
831
                        if (packLock != null)
 
832
                        {
 
833
                                packLock.Unlock();
 
834
                                packLock = null;
 
835
                        }
 
836
                }
 
837
 
 
838
                /// <summary>Generate an advertisement of available refs and capabilities.</summary>
 
839
                /// <remarks>Generate an advertisement of available refs and capabilities.</remarks>
 
840
                /// <param name="adv">the advertisement formatter.</param>
 
841
                /// <exception cref="System.IO.IOException">the formatter failed to write an advertisement.
 
842
                ///     </exception>
 
843
                public virtual void SendAdvertisedRefs(RefAdvertiser adv)
 
844
                {
 
845
                        if (advertiseError != null)
 
846
                        {
 
847
                                adv.WriteOne("ERR " + advertiseError);
 
848
                                return;
 
849
                        }
 
850
                        adv.Init(db);
 
851
                        adv.AdvertiseCapability(BasePackPushConnection.CAPABILITY_SIDE_BAND_64K);
 
852
                        adv.AdvertiseCapability(BasePackPushConnection.CAPABILITY_DELETE_REFS);
 
853
                        adv.AdvertiseCapability(BasePackPushConnection.CAPABILITY_REPORT_STATUS);
 
854
                        if (allowOfsDelta)
 
855
                        {
 
856
                                adv.AdvertiseCapability(BasePackPushConnection.CAPABILITY_OFS_DELTA);
 
857
                        }
 
858
                        adv.Send(GetAdvertisedRefs());
 
859
                        foreach (ObjectId obj in advertisedHaves)
 
860
                        {
 
861
                                adv.AdvertiseHave(obj);
 
862
                        }
 
863
                        if (adv.IsEmpty())
 
864
                        {
 
865
                                adv.AdvertiseId(ObjectId.ZeroId, "capabilities^{}");
 
866
                        }
 
867
                        adv.End();
 
868
                }
 
869
 
 
870
                /// <exception cref="System.IO.IOException"></exception>
 
871
                private void RecvCommands()
 
872
                {
 
873
                        for (; ; )
 
874
                        {
 
875
                                string line;
 
876
                                try
 
877
                                {
 
878
                                        line = pckIn.ReadStringRaw();
 
879
                                }
 
880
                                catch (EOFException eof)
 
881
                                {
 
882
                                        if (commands.IsEmpty())
 
883
                                        {
 
884
                                                return;
 
885
                                        }
 
886
                                        throw;
 
887
                                }
 
888
                                if (line == PacketLineIn.END)
 
889
                                {
 
890
                                        break;
 
891
                                }
 
892
                                if (commands.IsEmpty())
 
893
                                {
 
894
                                        int nul = line.IndexOf('\0');
 
895
                                        if (nul >= 0)
 
896
                                        {
 
897
                                                foreach (string c in Sharpen.Runtime.Substring(line, nul + 1).Split(" "))
 
898
                                                {
 
899
                                                        enabledCapablities.AddItem(c);
 
900
                                                }
 
901
                                                line = Sharpen.Runtime.Substring(line, 0, nul);
 
902
                                        }
 
903
                                }
 
904
                                if (line.Length < 83)
 
905
                                {
 
906
                                        string m = JGitText.Get().errorInvalidProtocolWantedOldNewRef;
 
907
                                        SendError(m);
 
908
                                        throw new PackProtocolException(m);
 
909
                                }
 
910
                                ObjectId oldId = ObjectId.FromString(Sharpen.Runtime.Substring(line, 0, 40));
 
911
                                ObjectId newId = ObjectId.FromString(Sharpen.Runtime.Substring(line, 41, 81));
 
912
                                string name = Sharpen.Runtime.Substring(line, 82);
 
913
                                ReceiveCommand cmd = new ReceiveCommand(oldId, newId, name);
 
914
                                if (name.Equals(Constants.HEAD))
 
915
                                {
 
916
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_CURRENT_BRANCH);
 
917
                                }
 
918
                                else
 
919
                                {
 
920
                                        cmd.SetRef(refs.Get(cmd.GetRefName()));
 
921
                                }
 
922
                                commands.AddItem(cmd);
 
923
                        }
 
924
                }
 
925
 
 
926
                private void EnableCapabilities()
 
927
                {
 
928
                        reportStatus = enabledCapablities.Contains(BasePackPushConnection.CAPABILITY_REPORT_STATUS
 
929
                                );
 
930
                        sideBand = enabledCapablities.Contains(BasePackPushConnection.CAPABILITY_SIDE_BAND_64K
 
931
                                );
 
932
                        if (sideBand)
 
933
                        {
 
934
                                OutputStream @out = rawOut;
 
935
                                rawOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA, SideBandOutputStream
 
936
                                        .MAX_BUF, @out);
 
937
                                msgOut = new SideBandOutputStream(SideBandOutputStream.CH_PROGRESS, SideBandOutputStream
 
938
                                        .MAX_BUF, @out);
 
939
                                pckOut = new PacketLineOut(rawOut);
 
940
                                pckOut.SetFlushOnEnd(false);
 
941
                        }
 
942
                }
 
943
 
 
944
                private bool NeedPack()
 
945
                {
 
946
                        foreach (ReceiveCommand cmd in commands)
 
947
                        {
 
948
                                if (cmd.GetType() != ReceiveCommand.Type.DELETE)
 
949
                                {
 
950
                                        return true;
 
951
                                }
 
952
                        }
 
953
                        return false;
 
954
                }
 
955
 
 
956
                /// <exception cref="System.IO.IOException"></exception>
 
957
                private void DoReceivePack()
 
958
                {
 
959
                        // It might take the client a while to pack the objects it needs
 
960
                        // to send to us.  We should increase our timeout so we don't
 
961
                        // abort while the client is computing.
 
962
                        //
 
963
                        if (timeoutIn != null)
 
964
                        {
 
965
                                timeoutIn.SetTimeout(10 * timeout * 1000);
 
966
                        }
 
967
                        ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
 
968
                        ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
 
969
                        if (sideBand)
 
970
                        {
 
971
                                resolving = new SideBandProgressMonitor(msgOut);
 
972
                        }
 
973
                        ObjectInserter ins = db.NewObjectInserter();
 
974
                        try
 
975
                        {
 
976
                                string lockMsg = "jgit receive-pack";
 
977
                                if (GetRefLogIdent() != null)
 
978
                                {
 
979
                                        lockMsg += " from " + GetRefLogIdent().ToExternalString();
 
980
                                }
 
981
                                parser = ins.NewPackParser(rawIn);
 
982
                                parser.SetAllowThin(true);
 
983
                                parser.SetNeedNewObjectIds(checkReferencedIsReachable);
 
984
                                parser.SetNeedBaseObjectIds(checkReferencedIsReachable);
 
985
                                parser.SetObjectChecking(IsCheckReceivedObjects());
 
986
                                parser.SetLockMessage(lockMsg);
 
987
                                packLock = parser.Parse(receiving, resolving);
 
988
                                ins.Flush();
 
989
                        }
 
990
                        finally
 
991
                        {
 
992
                                ins.Release();
 
993
                        }
 
994
                        if (timeoutIn != null)
 
995
                        {
 
996
                                timeoutIn.SetTimeout(timeout * 1000);
 
997
                        }
 
998
                }
 
999
 
 
1000
                private bool NeedCheckConnectivity()
 
1001
                {
 
1002
                        return IsCheckReceivedObjects() || IsCheckReferencedObjectsAreReachable();
 
1003
                }
 
1004
 
 
1005
                /// <exception cref="System.IO.IOException"></exception>
 
1006
                private void CheckConnectivity()
 
1007
                {
 
1008
                        ObjectIdSubclassMap<ObjectId> baseObjects = null;
 
1009
                        ObjectIdSubclassMap<ObjectId> providedObjects = null;
 
1010
                        if (checkReferencedIsReachable)
 
1011
                        {
 
1012
                                baseObjects = parser.GetBaseObjectIds();
 
1013
                                providedObjects = parser.GetNewObjectIds();
 
1014
                        }
 
1015
                        parser = null;
 
1016
                        ObjectWalk ow = new ObjectWalk(db);
 
1017
                        ow.SetRetainBody(false);
 
1018
                        if (checkReferencedIsReachable)
 
1019
                        {
 
1020
                                ow.Sort(RevSort.TOPO);
 
1021
                                if (!baseObjects.IsEmpty())
 
1022
                                {
 
1023
                                        ow.Sort(RevSort.BOUNDARY, true);
 
1024
                                }
 
1025
                        }
 
1026
                        foreach (ReceiveCommand cmd in commands)
 
1027
                        {
 
1028
                                if (cmd.GetResult() != ReceiveCommand.Result.NOT_ATTEMPTED)
 
1029
                                {
 
1030
                                        continue;
 
1031
                                }
 
1032
                                if (cmd.GetType() == ReceiveCommand.Type.DELETE)
 
1033
                                {
 
1034
                                        continue;
 
1035
                                }
 
1036
                                ow.MarkStart(ow.ParseAny(cmd.GetNewId()));
 
1037
                        }
 
1038
                        foreach (ObjectId have in advertisedHaves)
 
1039
                        {
 
1040
                                RevObject o = ow.ParseAny(have);
 
1041
                                ow.MarkUninteresting(o);
 
1042
                                if (checkReferencedIsReachable && !baseObjects.IsEmpty())
 
1043
                                {
 
1044
                                        o = ow.Peel(o);
 
1045
                                        if (o is RevCommit)
 
1046
                                        {
 
1047
                                                o = ((RevCommit)o).Tree;
 
1048
                                        }
 
1049
                                        if (o is RevTree)
 
1050
                                        {
 
1051
                                                ow.MarkUninteresting(o);
 
1052
                                        }
 
1053
                                }
 
1054
                        }
 
1055
                        RevCommit c;
 
1056
                        while ((c = ow.Next()) != null)
 
1057
                        {
 
1058
                                if (checkReferencedIsReachable && !c.Has(RevFlag.UNINTERESTING) && !providedObjects
 
1059
                                        .Contains(c))
 
1060
                                {
 
1061
                                        //
 
1062
                                        //
 
1063
                                        throw new MissingObjectException(c, Constants.TYPE_COMMIT);
 
1064
                                }
 
1065
                        }
 
1066
                        RevObject o_1;
 
1067
                        while ((o_1 = ow.NextObject()) != null)
 
1068
                        {
 
1069
                                if (o_1.Has(RevFlag.UNINTERESTING))
 
1070
                                {
 
1071
                                        continue;
 
1072
                                }
 
1073
                                if (checkReferencedIsReachable)
 
1074
                                {
 
1075
                                        if (providedObjects.Contains(o_1))
 
1076
                                        {
 
1077
                                                continue;
 
1078
                                        }
 
1079
                                        else
 
1080
                                        {
 
1081
                                                throw new MissingObjectException(o_1, o_1.Type);
 
1082
                                        }
 
1083
                                }
 
1084
                                if (o_1 is RevBlob && !db.HasObject(o_1))
 
1085
                                {
 
1086
                                        throw new MissingObjectException(o_1, Constants.TYPE_BLOB);
 
1087
                                }
 
1088
                        }
 
1089
                        if (checkReferencedIsReachable)
 
1090
                        {
 
1091
                                foreach (ObjectId id in baseObjects)
 
1092
                                {
 
1093
                                        o_1 = ow.ParseAny(id);
 
1094
                                        if (!o_1.Has(RevFlag.UNINTERESTING))
 
1095
                                        {
 
1096
                                                throw new MissingObjectException(o_1, o_1.Type);
 
1097
                                        }
 
1098
                                }
 
1099
                        }
 
1100
                }
 
1101
 
 
1102
                private void ValidateCommands()
 
1103
                {
 
1104
                        foreach (ReceiveCommand cmd in commands)
 
1105
                        {
 
1106
                                Ref @ref = cmd.GetRef();
 
1107
                                if (cmd.GetResult() != ReceiveCommand.Result.NOT_ATTEMPTED)
 
1108
                                {
 
1109
                                        continue;
 
1110
                                }
 
1111
                                if (cmd.GetType() == ReceiveCommand.Type.DELETE && !IsAllowDeletes())
 
1112
                                {
 
1113
                                        // Deletes are not supported on this repository.
 
1114
                                        //
 
1115
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_NODELETE);
 
1116
                                        continue;
 
1117
                                }
 
1118
                                if (cmd.GetType() == ReceiveCommand.Type.CREATE)
 
1119
                                {
 
1120
                                        if (!IsAllowCreates())
 
1121
                                        {
 
1122
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_NOCREATE);
 
1123
                                                continue;
 
1124
                                        }
 
1125
                                        if (@ref != null && !IsAllowNonFastForwards())
 
1126
                                        {
 
1127
                                                // Creation over an existing ref is certainly not going
 
1128
                                                // to be a fast-forward update. We can reject it early.
 
1129
                                                //
 
1130
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_NONFASTFORWARD);
 
1131
                                                continue;
 
1132
                                        }
 
1133
                                        if (@ref != null)
 
1134
                                        {
 
1135
                                                // A well behaved client shouldn't have sent us a
 
1136
                                                // create command for a ref we advertised to it.
 
1137
                                                //
 
1138
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, "ref exists");
 
1139
                                                continue;
 
1140
                                        }
 
1141
                                }
 
1142
                                if (cmd.GetType() == ReceiveCommand.Type.DELETE && @ref != null && !ObjectId.ZeroId
 
1143
                                        .Equals(cmd.GetOldId()) && !@ref.GetObjectId().Equals(cmd.GetOldId()))
 
1144
                                {
 
1145
                                        // Delete commands can be sent with the old id matching our
 
1146
                                        // advertised value, *OR* with the old id being 0{40}. Any
 
1147
                                        // other requested old id is invalid.
 
1148
                                        //
 
1149
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, JGitText.Get().invalidOldIdSent
 
1150
                                                );
 
1151
                                        continue;
 
1152
                                }
 
1153
                                if (cmd.GetType() == ReceiveCommand.Type.UPDATE)
 
1154
                                {
 
1155
                                        if (@ref == null)
 
1156
                                        {
 
1157
                                                // The ref must have been advertised in order to be updated.
 
1158
                                                //
 
1159
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, JGitText.Get().noSuchRef
 
1160
                                                        );
 
1161
                                                continue;
 
1162
                                        }
 
1163
                                        if (!@ref.GetObjectId().Equals(cmd.GetOldId()))
 
1164
                                        {
 
1165
                                                // A properly functioning client will send the same
 
1166
                                                // object id we advertised.
 
1167
                                                //
 
1168
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, JGitText.Get().invalidOldIdSent
 
1169
                                                        );
 
1170
                                                continue;
 
1171
                                        }
 
1172
                                        // Is this possibly a non-fast-forward style update?
 
1173
                                        //
 
1174
                                        RevObject oldObj;
 
1175
                                        RevObject newObj;
 
1176
                                        try
 
1177
                                        {
 
1178
                                                oldObj = walk.ParseAny(cmd.GetOldId());
 
1179
                                        }
 
1180
                                        catch (IOException)
 
1181
                                        {
 
1182
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT, cmd.GetOldId().Name);
 
1183
                                                continue;
 
1184
                                        }
 
1185
                                        try
 
1186
                                        {
 
1187
                                                newObj = walk.ParseAny(cmd.GetNewId());
 
1188
                                        }
 
1189
                                        catch (IOException)
 
1190
                                        {
 
1191
                                                cmd.SetResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT, cmd.GetNewId().Name);
 
1192
                                                continue;
 
1193
                                        }
 
1194
                                        if (oldObj is RevCommit && newObj is RevCommit)
 
1195
                                        {
 
1196
                                                try
 
1197
                                                {
 
1198
                                                        if (!walk.IsMergedInto((RevCommit)oldObj, (RevCommit)newObj))
 
1199
                                                        {
 
1200
                                                                cmd.SetType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
 
1201
                                                        }
 
1202
                                                }
 
1203
                                                catch (MissingObjectException e)
 
1204
                                                {
 
1205
                                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT, e.Message);
 
1206
                                                }
 
1207
                                                catch (IOException)
 
1208
                                                {
 
1209
                                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON);
 
1210
                                                }
 
1211
                                        }
 
1212
                                        else
 
1213
                                        {
 
1214
                                                cmd.SetType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
 
1215
                                        }
 
1216
                                }
 
1217
                                if (!cmd.GetRefName().StartsWith(Constants.R_REFS) || !Repository.IsValidRefName(
 
1218
                                        cmd.GetRefName()))
 
1219
                                {
 
1220
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, JGitText.Get().funnyRefname
 
1221
                                                );
 
1222
                                }
 
1223
                        }
 
1224
                }
 
1225
 
 
1226
                private void ExecuteCommands()
 
1227
                {
 
1228
                        preReceive.OnPreReceive(this, FilterCommands(ReceiveCommand.Result.NOT_ATTEMPTED)
 
1229
                                );
 
1230
                        foreach (ReceiveCommand cmd in FilterCommands(ReceiveCommand.Result.NOT_ATTEMPTED
 
1231
                                ))
 
1232
                        {
 
1233
                                Execute(cmd);
 
1234
                        }
 
1235
                }
 
1236
 
 
1237
                private void Execute(ReceiveCommand cmd)
 
1238
                {
 
1239
                        try
 
1240
                        {
 
1241
                                RefUpdate ru = db.UpdateRef(cmd.GetRefName());
 
1242
                                ru.SetRefLogIdent(GetRefLogIdent());
 
1243
                                switch (cmd.GetType())
 
1244
                                {
 
1245
                                        case ReceiveCommand.Type.DELETE:
 
1246
                                        {
 
1247
                                                if (!ObjectId.ZeroId.Equals(cmd.GetOldId()))
 
1248
                                                {
 
1249
                                                        // We can only do a CAS style delete if the client
 
1250
                                                        // didn't bork its delete request by sending the
 
1251
                                                        // wrong zero id rather than the advertised one.
 
1252
                                                        //
 
1253
                                                        ru.SetExpectedOldObjectId(cmd.GetOldId());
 
1254
                                                }
 
1255
                                                ru.SetForceUpdate(true);
 
1256
                                                Status(cmd, ru.Delete(walk));
 
1257
                                                break;
 
1258
                                        }
 
1259
 
 
1260
                                        case ReceiveCommand.Type.CREATE:
 
1261
                                        case ReceiveCommand.Type.UPDATE:
 
1262
                                        case ReceiveCommand.Type.UPDATE_NONFASTFORWARD:
 
1263
                                        {
 
1264
                                                ru.SetForceUpdate(IsAllowNonFastForwards());
 
1265
                                                ru.SetExpectedOldObjectId(cmd.GetOldId());
 
1266
                                                ru.SetNewObjectId(cmd.GetNewId());
 
1267
                                                ru.SetRefLogMessage("push", true);
 
1268
                                                Status(cmd, ru.Update(walk));
 
1269
                                                break;
 
1270
                                        }
 
1271
                                }
 
1272
                        }
 
1273
                        catch (IOException err)
 
1274
                        {
 
1275
                                cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, MessageFormat.Format(JGitText
 
1276
                                        .Get().lockError, err.Message));
 
1277
                        }
 
1278
                }
 
1279
 
 
1280
                private void Status(ReceiveCommand cmd, RefUpdate.Result result)
 
1281
                {
 
1282
                        switch (result)
 
1283
                        {
 
1284
                                case RefUpdate.Result.NOT_ATTEMPTED:
 
1285
                                {
 
1286
                                        cmd.SetResult(ReceiveCommand.Result.NOT_ATTEMPTED);
 
1287
                                        break;
 
1288
                                }
 
1289
 
 
1290
                                case RefUpdate.Result.LOCK_FAILURE:
 
1291
                                case RefUpdate.Result.IO_FAILURE:
 
1292
                                {
 
1293
                                        cmd.SetResult(ReceiveCommand.Result.LOCK_FAILURE);
 
1294
                                        break;
 
1295
                                }
 
1296
 
 
1297
                                case RefUpdate.Result.NO_CHANGE:
 
1298
                                case RefUpdate.Result.NEW:
 
1299
                                case RefUpdate.Result.FORCED:
 
1300
                                case RefUpdate.Result.FAST_FORWARD:
 
1301
                                {
 
1302
                                        cmd.SetResult(ReceiveCommand.Result.OK);
 
1303
                                        break;
 
1304
                                }
 
1305
 
 
1306
                                case RefUpdate.Result.REJECTED:
 
1307
                                {
 
1308
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_NONFASTFORWARD);
 
1309
                                        break;
 
1310
                                }
 
1311
 
 
1312
                                case RefUpdate.Result.REJECTED_CURRENT_BRANCH:
 
1313
                                {
 
1314
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_CURRENT_BRANCH);
 
1315
                                        break;
 
1316
                                }
 
1317
 
 
1318
                                default:
 
1319
                                {
 
1320
                                        cmd.SetResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, result.ToString());
 
1321
                                        break;
 
1322
                                        break;
 
1323
                                }
 
1324
                        }
 
1325
                }
 
1326
 
 
1327
                private IList<ReceiveCommand> FilterCommands(ReceiveCommand.Result want)
 
1328
                {
 
1329
                        IList<ReceiveCommand> r = new AList<ReceiveCommand>(commands.Count);
 
1330
                        foreach (ReceiveCommand cmd in commands)
 
1331
                        {
 
1332
                                if (cmd.GetResult() == want)
 
1333
                                {
 
1334
                                        r.AddItem(cmd);
 
1335
                                }
 
1336
                        }
 
1337
                        return r;
 
1338
                }
 
1339
 
 
1340
                /// <exception cref="System.IO.IOException"></exception>
 
1341
                private void SendStatusReport(bool forClient, ReceivePack.Reporter @out)
 
1342
                {
 
1343
                        if (unpackError != null)
 
1344
                        {
 
1345
                                @out.SendString("unpack error " + unpackError.Message);
 
1346
                                if (forClient)
 
1347
                                {
 
1348
                                        foreach (ReceiveCommand cmd in commands)
 
1349
                                        {
 
1350
                                                @out.SendString("ng " + cmd.GetRefName() + " n/a (unpacker error)");
 
1351
                                        }
 
1352
                                }
 
1353
                                return;
 
1354
                        }
 
1355
                        if (forClient)
 
1356
                        {
 
1357
                                @out.SendString("unpack ok");
 
1358
                        }
 
1359
                        foreach (ReceiveCommand cmd_1 in commands)
 
1360
                        {
 
1361
                                if (cmd_1.GetResult() == ReceiveCommand.Result.OK)
 
1362
                                {
 
1363
                                        if (forClient)
 
1364
                                        {
 
1365
                                                @out.SendString("ok " + cmd_1.GetRefName());
 
1366
                                        }
 
1367
                                        continue;
 
1368
                                }
 
1369
                                StringBuilder r = new StringBuilder();
 
1370
                                r.Append("ng ");
 
1371
                                r.Append(cmd_1.GetRefName());
 
1372
                                r.Append(" ");
 
1373
                                switch (cmd_1.GetResult())
 
1374
                                {
 
1375
                                        case ReceiveCommand.Result.NOT_ATTEMPTED:
 
1376
                                        {
 
1377
                                                r.Append("server bug; ref not processed");
 
1378
                                                break;
 
1379
                                        }
 
1380
 
 
1381
                                        case ReceiveCommand.Result.REJECTED_NOCREATE:
 
1382
                                        {
 
1383
                                                r.Append("creation prohibited");
 
1384
                                                break;
 
1385
                                        }
 
1386
 
 
1387
                                        case ReceiveCommand.Result.REJECTED_NODELETE:
 
1388
                                        {
 
1389
                                                r.Append("deletion prohibited");
 
1390
                                                break;
 
1391
                                        }
 
1392
 
 
1393
                                        case ReceiveCommand.Result.REJECTED_NONFASTFORWARD:
 
1394
                                        {
 
1395
                                                r.Append("non-fast forward");
 
1396
                                                break;
 
1397
                                        }
 
1398
 
 
1399
                                        case ReceiveCommand.Result.REJECTED_CURRENT_BRANCH:
 
1400
                                        {
 
1401
                                                r.Append("branch is currently checked out");
 
1402
                                                break;
 
1403
                                        }
 
1404
 
 
1405
                                        case ReceiveCommand.Result.REJECTED_MISSING_OBJECT:
 
1406
                                        {
 
1407
                                                if (cmd_1.GetMessage() == null)
 
1408
                                                {
 
1409
                                                        r.Append("missing object(s)");
 
1410
                                                }
 
1411
                                                else
 
1412
                                                {
 
1413
                                                        if (cmd_1.GetMessage().Length == Constants.OBJECT_ID_STRING_LENGTH)
 
1414
                                                        {
 
1415
                                                                r.Append("object " + cmd_1.GetMessage() + " missing");
 
1416
                                                        }
 
1417
                                                        else
 
1418
                                                        {
 
1419
                                                                r.Append(cmd_1.GetMessage());
 
1420
                                                        }
 
1421
                                                }
 
1422
                                                break;
 
1423
                                        }
 
1424
 
 
1425
                                        case ReceiveCommand.Result.REJECTED_OTHER_REASON:
 
1426
                                        {
 
1427
                                                if (cmd_1.GetMessage() == null)
 
1428
                                                {
 
1429
                                                        r.Append("unspecified reason");
 
1430
                                                }
 
1431
                                                else
 
1432
                                                {
 
1433
                                                        r.Append(cmd_1.GetMessage());
 
1434
                                                }
 
1435
                                                break;
 
1436
                                        }
 
1437
 
 
1438
                                        case ReceiveCommand.Result.LOCK_FAILURE:
 
1439
                                        {
 
1440
                                                r.Append("failed to lock");
 
1441
                                                break;
 
1442
                                        }
 
1443
 
 
1444
                                        case ReceiveCommand.Result.OK:
 
1445
                                        {
 
1446
                                                // We shouldn't have reached this case (see 'ok' case above).
 
1447
                                                continue;
 
1448
                                        }
 
1449
                                }
 
1450
                                @out.SendString(r.ToString());
 
1451
                        }
 
1452
                }
 
1453
 
 
1454
                internal abstract class Reporter
 
1455
                {
 
1456
                        /// <exception cref="System.IO.IOException"></exception>
 
1457
                        internal abstract void SendString(string s);
 
1458
                }
 
1459
        }
 
1460
}