Skip to main content

git_zero_server/receive/
mod.rs

1//! Receive-pack wire handling. The host authenticates requests, quarantines
2//! objects, resolves deltas, validates graph/ref updates, and publishes them.
3//! This module never acknowledges a push or changes refs on its own.
4use git_zero_core::{Error, ObjectFormat, Result, io::Write};
5
6mod advertisement;
7mod ancestry;
8mod attributes;
9mod control;
10mod gitmodules;
11mod headers;
12mod report;
13mod tree;
14pub use advertisement::{Advertisement, write_advertisement};
15pub use ancestry::Ancestry;
16pub use attributes::FsckAttributes;
17pub use control::{Command, ControlDecoder, ControlEvent, ControlProgress, Limits};
18pub use gitmodules::FsckModules;
19pub use headers::FsckHeaders;
20pub use report::{Report, Status, write_progress};
21pub use tree::{BlobChecks, check_tree_entry};
22
23const FLAGS: &[(&[u8], u16)] = &[
24    (b"report-status", 1),
25    (b"report-status-v2", 2),
26    (b"delete-refs", 4),
27    (b"ofs-delta", 8),
28    (b"atomic", 16),
29    (b"push-options", 32),
30    (b"side-band-64k", 64),
31    (b"quiet", 128),
32];
33
34/// Capabilities offered by a host or selected by a client. Hosts must opt in
35/// only when their publication/option/progress handling fulfills the promise.
36#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
37pub struct Capabilities(u16);
38
39impl Capabilities {
40    pub const REPORT_STATUS: Self = Self(1);
41    pub const REPORT_STATUS_V2: Self = Self(2);
42    pub const DELETE_REFS: Self = Self(4);
43    pub const OFS_DELTA: Self = Self(8);
44    pub const ATOMIC: Self = Self(16);
45    pub const PUSH_OPTIONS: Self = Self(32);
46    pub const SIDE_BAND_64K: Self = Self(64);
47    pub const QUIET: Self = Self(128);
48
49    pub const fn contains(self, capability: Self) -> bool {
50        self.0 & capability.0 == capability.0
51    }
52
53    fn parse(bytes: &[u8], format: ObjectFormat, offered: Self) -> Result<Self> {
54        let mut selected = 0;
55        let mut format_seen = false;
56        let mut agent_seen = false;
57        for token in bytes
58            .split(|&byte| byte == b' ')
59            .filter(|token| !token.is_empty())
60        {
61            if token == format.capability() {
62                if format_seen {
63                    return Err(Error::InvalidRequest);
64                }
65                format_seen = true;
66                continue;
67            }
68            if let Some(agent) = token.strip_prefix(b"agent=") {
69                if agent_seen
70                    || agent.is_empty()
71                    || !agent.iter().all(|byte| byte.is_ascii_graphic())
72                {
73                    return Err(Error::InvalidRequest);
74                }
75                agent_seen = true;
76                continue;
77            }
78            let flag = FLAGS
79                .iter()
80                .find_map(|&(name, flag)| (name == token).then_some(flag))
81                .ok_or(Error::Unsupported)?;
82            if offered.0 & flag == 0 || selected & flag != 0 {
83                return Err(Error::InvalidRequest);
84            }
85            selected |= flag;
86        }
87        if format == ObjectFormat::Sha256 && !format_seen {
88            return Err(Error::InvalidRequest);
89        }
90        Ok(Self(selected))
91    }
92
93    fn write(self, format: ObjectFormat, output: &mut impl Write) -> Result<()> {
94        output.write_all(format.capability())?;
95        for &(name, flag) in FLAGS {
96            if self.0 & flag != 0 {
97                output.write_all(b" ")?;
98                output.write_all(name)?;
99            }
100        }
101        output.write_all(b" agent=git-zero")
102    }
103}
104
105impl core::ops::BitOr for Capabilities {
106    type Output = Self;
107    fn bitor(self, rhs: Self) -> Self {
108        Self(self.0 | rhs.0)
109    }
110}