summaryrefslogtreecommitdiff
path: root/protocol-caps.c
blob: 27e0f85b100cb9e6d4f47c495c1be2fcf520c933 (plain)
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
#include "git-compat-util.h"
#include "protocol-caps.h"
#include "gettext.h"
#include "hex.h"
#include "pkt-line.h"
#include "hash.h"
#include "object.h"
#include "odb.h"
#include "repository.h"
#include "string-list.h"
#include "strbuf.h"

struct requested_info {
	unsigned size:1;
	unsigned type:1;
};

/*
 * Parses oids from the given line and collects them in the given
 * oid_str_list. Returns 1 if parsing was successful and 0 otherwise.
 */
static int parse_oid(const char *line, struct string_list *oid_str_list)
{
	const char *arg;

	if (!skip_prefix(line, "oid ", &arg))
		return 0;

	string_list_append(oid_str_list, arg);

	return 1;
}

/*
 * odb_read_object_info_extended() wrapper. Similar to odb_read_object_info()
 * but uses the flags:
 *
 * - OBJECT_INFO_SKIP_FETCH_OBJECT so a server won't fetch an object when a
 *   object-info request asks for an OID that it doesn't have.
 *
 * - OBJECT_INFO_QUICK to avoid re-scanning packs when the object is not found.
 */
static enum object_type get_object_info(struct object_database *odb,
			   const struct object_id *oid,
			   size_t *sizep)
{
	enum object_type type;
	struct object_info oi = OBJECT_INFO_INIT;

	oi.typep = &type;
	oi.sizep = sizep;
	if (odb_read_object_info_extended(odb, oid, &oi,
					  OBJECT_INFO_LOOKUP_REPLACE |
					  OBJECT_INFO_SKIP_FETCH_OBJECT |
					  OBJECT_INFO_QUICK) < 0)
		return OBJ_BAD;
	return type;
}

/*
 * Validates and send requested info back to the client. Any errors detected
 * are returned as they are detected.
 */
static void send_info(struct repository *r, struct packet_writer *writer,
		      struct string_list *oid_str_list,
		      struct requested_info *info)
{
	struct string_list_item *item;
	struct strbuf send_buffer = STRBUF_INIT;

	if (!oid_str_list->nr)
		return;

	if (info->size)
		packet_writer_write(writer, "size");

	if (info->type)
		packet_writer_write(writer, "type");

	for_each_string_list_item (item, oid_str_list) {
		const char *oid_str = item->string;
		enum object_type object_type;
		struct object_id oid;
		size_t object_size;

		if (get_oid_hex_algop(oid_str, &oid, r->hash_algo) < 0) {
			packet_writer_error(
				writer,
				"object-info: protocol error, expected to get "
				"oid, not '%s'",
				oid_str);
			continue;
		}

		strbuf_addstr(&send_buffer, oid_str);

		/*
		 * Check the existence of the object first.
		 * If an object is not recognized by the server append SP to
		 * the response.
		 */
		object_type = get_object_info(r->objects, &oid, &object_size);
		if (object_type <= OBJ_NONE) {
			strbuf_addstr(&send_buffer, " ");
			goto write;
		}

		if (info->size) {
			strbuf_addf(&send_buffer, " %"PRIuMAX,
				    (uintmax_t)object_size);
		}

		if (info->type)
			strbuf_addf(&send_buffer, " %s", type_name(object_type));

write:
		packet_writer_write(writer, "%s", send_buffer.buf);
		strbuf_reset(&send_buffer);
	}
	strbuf_release(&send_buffer);
}

int cap_object_info(struct repository *r, struct packet_reader *request)
{
	struct requested_info info = { 0 };
	struct packet_writer writer;
	struct string_list oid_str_list = STRING_LIST_INIT_DUP;

	packet_writer_init(&writer, 1);

	while (packet_reader_read(request) == PACKET_READ_NORMAL) {
		if (!strcmp("size", request->line)) {
			info.size = 1;
			continue;
		}

		if (!strcmp("type", request->line)) {
			info.type = 1;
			continue;
		}

		if (parse_oid(request->line, &oid_str_list))
			continue;

		packet_writer_error(&writer,
				    "object-info: unexpected line: '%s'",
				    request->line);
	}

	if (request->status != PACKET_READ_FLUSH) {
		packet_writer_error(
			&writer, "object-info: expected flush after arguments");
		die(_("object-info: expected flush after arguments"));
	}

	send_info(r, &writer, &oid_str_list, &info);

	string_list_clear(&oid_str_list, 1);

	packet_flush(1);

	return 0;
}