1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "tag.h"
9 
10 #include "commit.h"
11 #include "signature.h"
12 #include "message.h"
13 #include "wildmatch.h"
14 #include "git2/object.h"
15 #include "git2/repository.h"
16 #include "git2/signature.h"
17 #include "git2/odb_backend.h"
18 
git_tag__free(void * _tag)19 void git_tag__free(void *_tag)
20 {
21 	git_tag *tag = _tag;
22 	git_signature_free(tag->tagger);
23 	git__free(tag->message);
24 	git__free(tag->tag_name);
25 	git__free(tag);
26 }
27 
git_tag_target(git_object ** target,const git_tag * t)28 int git_tag_target(git_object **target, const git_tag *t)
29 {
30 	assert(t);
31 	return git_object_lookup(target, t->object.repo, &t->target, t->type);
32 }
33 
git_tag_target_id(const git_tag * t)34 const git_oid *git_tag_target_id(const git_tag *t)
35 {
36 	assert(t);
37 	return &t->target;
38 }
39 
git_tag_target_type(const git_tag * t)40 git_object_t git_tag_target_type(const git_tag *t)
41 {
42 	assert(t);
43 	return t->type;
44 }
45 
git_tag_name(const git_tag * t)46 const char *git_tag_name(const git_tag *t)
47 {
48 	assert(t);
49 	return t->tag_name;
50 }
51 
git_tag_tagger(const git_tag * t)52 const git_signature *git_tag_tagger(const git_tag *t)
53 {
54 	return t->tagger;
55 }
56 
git_tag_message(const git_tag * t)57 const char *git_tag_message(const git_tag *t)
58 {
59 	assert(t);
60 	return t->message;
61 }
62 
tag_error(const char * str)63 static int tag_error(const char *str)
64 {
65 	git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
66 	return -1;
67 }
68 
tag_parse(git_tag * tag,const char * buffer,const char * buffer_end)69 static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
70 {
71 	static const char *tag_types[] = {
72 		NULL, "commit\n", "tree\n", "blob\n", "tag\n"
73 	};
74 	size_t text_len, alloc_len;
75 	const char *search;
76 	unsigned int i;
77 
78 	if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
79 		return tag_error("object field invalid");
80 
81 	if (buffer + 5 >= buffer_end)
82 		return tag_error("object too short");
83 
84 	if (memcmp(buffer, "type ", 5) != 0)
85 		return tag_error("type field not found");
86 	buffer += 5;
87 
88 	tag->type = GIT_OBJECT_INVALID;
89 
90 	for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
91 		size_t type_length = strlen(tag_types[i]);
92 
93 		if (buffer + type_length >= buffer_end)
94 			return tag_error("object too short");
95 
96 		if (memcmp(buffer, tag_types[i], type_length) == 0) {
97 			tag->type = i;
98 			buffer += type_length;
99 			break;
100 		}
101 	}
102 
103 	if (tag->type == GIT_OBJECT_INVALID)
104 		return tag_error("invalid object type");
105 
106 	if (buffer + 4 >= buffer_end)
107 		return tag_error("object too short");
108 
109 	if (memcmp(buffer, "tag ", 4) != 0)
110 		return tag_error("tag field not found");
111 
112 	buffer += 4;
113 
114 	search = memchr(buffer, '\n', buffer_end - buffer);
115 	if (search == NULL)
116 		return tag_error("object too short");
117 
118 	text_len = search - buffer;
119 
120 	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
121 	tag->tag_name = git__malloc(alloc_len);
122 	GIT_ERROR_CHECK_ALLOC(tag->tag_name);
123 
124 	memcpy(tag->tag_name, buffer, text_len);
125 	tag->tag_name[text_len] = '\0';
126 
127 	buffer = search + 1;
128 
129 	tag->tagger = NULL;
130 	if (buffer < buffer_end && *buffer != '\n') {
131 		tag->tagger = git__malloc(sizeof(git_signature));
132 		GIT_ERROR_CHECK_ALLOC(tag->tagger);
133 
134 		if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
135 			return -1;
136 	}
137 
138 	tag->message = NULL;
139 	if (buffer < buffer_end) {
140 		/* If we're not at the end of the header, search for it */
141 		if(*buffer != '\n') {
142 			search = git__memmem(buffer, buffer_end - buffer,
143 					     "\n\n", 2);
144 			if (search)
145 				buffer = search + 1;
146 			else
147 				return tag_error("tag contains no message");
148 		}
149 
150 		text_len = buffer_end - ++buffer;
151 
152 		GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
153 		tag->message = git__malloc(alloc_len);
154 		GIT_ERROR_CHECK_ALLOC(tag->message);
155 
156 		memcpy(tag->message, buffer, text_len);
157 		tag->message[text_len] = '\0';
158 	}
159 
160 	return 0;
161 }
162 
git_tag__parse_raw(void * _tag,const char * data,size_t size)163 int git_tag__parse_raw(void *_tag, const char *data, size_t size)
164 {
165 	return tag_parse(_tag, data, data + size);
166 }
167 
git_tag__parse(void * _tag,git_odb_object * odb_obj)168 int git_tag__parse(void *_tag, git_odb_object *odb_obj)
169 {
170 	git_tag *tag = _tag;
171 	const char *buffer = git_odb_object_data(odb_obj);
172 	const char *buffer_end = buffer + git_odb_object_size(odb_obj);
173 
174 	return tag_parse(tag, buffer, buffer_end);
175 }
176 
retrieve_tag_reference(git_reference ** tag_reference_out,git_buf * ref_name_out,git_repository * repo,const char * tag_name)177 static int retrieve_tag_reference(
178 	git_reference **tag_reference_out,
179 	git_buf *ref_name_out,
180 	git_repository *repo,
181 	const char *tag_name)
182 {
183 	git_reference *tag_ref;
184 	int error;
185 
186 	*tag_reference_out = NULL;
187 
188 	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
189 		return -1;
190 
191 	error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
192 	if (error < 0)
193 		return error; /* Be it not foundo or corrupted */
194 
195 	*tag_reference_out = tag_ref;
196 
197 	return 0;
198 }
199 
retrieve_tag_reference_oid(git_oid * oid,git_buf * ref_name_out,git_repository * repo,const char * tag_name)200 static int retrieve_tag_reference_oid(
201 	git_oid *oid,
202 	git_buf *ref_name_out,
203 	git_repository *repo,
204 	const char *tag_name)
205 {
206 	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
207 		return -1;
208 
209 	return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
210 }
211 
write_tag_annotation(git_oid * oid,git_repository * repo,const char * tag_name,const git_object * target,const git_signature * tagger,const char * message)212 static int write_tag_annotation(
213 		git_oid *oid,
214 		git_repository *repo,
215 		const char *tag_name,
216 		const git_object *target,
217 		const git_signature *tagger,
218 		const char *message)
219 {
220 	git_buf tag = GIT_BUF_INIT;
221 	git_odb *odb;
222 
223 	git_oid__writebuf(&tag, "object ", git_object_id(target));
224 	git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
225 	git_buf_printf(&tag, "tag %s\n", tag_name);
226 	git_signature__writebuf(&tag, "tagger ", tagger);
227 	git_buf_putc(&tag, '\n');
228 
229 	if (git_buf_puts(&tag, message) < 0)
230 		goto on_error;
231 
232 	if (git_repository_odb__weakptr(&odb, repo) < 0)
233 		goto on_error;
234 
235 	if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0)
236 		goto on_error;
237 
238 	git_buf_dispose(&tag);
239 	return 0;
240 
241 on_error:
242 	git_buf_dispose(&tag);
243 	git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
244 	return -1;
245 }
246 
git_tag_create__internal(git_oid * oid,git_repository * repo,const char * tag_name,const git_object * target,const git_signature * tagger,const char * message,int allow_ref_overwrite,int create_tag_annotation)247 static int git_tag_create__internal(
248 		git_oid *oid,
249 		git_repository *repo,
250 		const char *tag_name,
251 		const git_object *target,
252 		const git_signature *tagger,
253 		const char *message,
254 		int allow_ref_overwrite,
255 		int create_tag_annotation)
256 {
257 	git_reference *new_ref = NULL;
258 	git_buf ref_name = GIT_BUF_INIT;
259 
260 	int error;
261 
262 	assert(repo && tag_name && target);
263 	assert(!create_tag_annotation || (tagger && message));
264 
265 	if (git_object_owner(target) != repo) {
266 		git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
267 		return -1;
268 	}
269 
270 	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
271 	if (error < 0 && error != GIT_ENOTFOUND)
272 		goto cleanup;
273 
274 	/** Ensure the tag name doesn't conflict with an already existing
275 	 *	reference unless overwriting has explicitly been requested **/
276 	if (error == 0 && !allow_ref_overwrite) {
277 		git_buf_dispose(&ref_name);
278 		git_error_set(GIT_ERROR_TAG, "tag already exists");
279 		return GIT_EEXISTS;
280 	}
281 
282 	if (create_tag_annotation) {
283 		if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
284 			return -1;
285 	} else
286 		git_oid_cpy(oid, git_object_id(target));
287 
288 	error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
289 
290 cleanup:
291 	git_reference_free(new_ref);
292 	git_buf_dispose(&ref_name);
293 	return error;
294 }
295 
git_tag_create(git_oid * oid,git_repository * repo,const char * tag_name,const git_object * target,const git_signature * tagger,const char * message,int allow_ref_overwrite)296 int git_tag_create(
297 	git_oid *oid,
298 	git_repository *repo,
299 	const char *tag_name,
300 	const git_object *target,
301 	const git_signature *tagger,
302 	const char *message,
303 	int allow_ref_overwrite)
304 {
305 	return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
306 }
307 
git_tag_annotation_create(git_oid * oid,git_repository * repo,const char * tag_name,const git_object * target,const git_signature * tagger,const char * message)308 int git_tag_annotation_create(
309 	git_oid *oid,
310 	git_repository *repo,
311 	const char *tag_name,
312 	const git_object *target,
313 	const git_signature *tagger,
314 	const char *message)
315 {
316 	assert(oid && repo && tag_name && target && tagger && message);
317 
318 	return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
319 }
320 
git_tag_create_lightweight(git_oid * oid,git_repository * repo,const char * tag_name,const git_object * target,int allow_ref_overwrite)321 int git_tag_create_lightweight(
322 	git_oid *oid,
323 	git_repository *repo,
324 	const char *tag_name,
325 	const git_object *target,
326 	int allow_ref_overwrite)
327 {
328 	return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
329 }
330 
git_tag_create_from_buffer(git_oid * oid,git_repository * repo,const char * buffer,int allow_ref_overwrite)331 int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
332 {
333 	git_tag tag;
334 	int error;
335 	git_odb *odb;
336 	git_odb_stream *stream;
337 	git_odb_object *target_obj;
338 
339 	git_reference *new_ref = NULL;
340 	git_buf ref_name = GIT_BUF_INIT;
341 
342 	assert(oid && buffer);
343 
344 	memset(&tag, 0, sizeof(tag));
345 
346 	if (git_repository_odb__weakptr(&odb, repo) < 0)
347 		return -1;
348 
349 	/* validate the buffer */
350 	if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
351 		return -1;
352 
353 	/* validate the target */
354 	if (git_odb_read(&target_obj, odb, &tag.target) < 0)
355 		goto on_error;
356 
357 	if (tag.type != target_obj->cached.type) {
358 		git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
359 		goto on_error;
360 	}
361 
362 	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
363 	if (error < 0 && error != GIT_ENOTFOUND)
364 		goto on_error;
365 
366 	/* We don't need these objects after this */
367 	git_signature_free(tag.tagger);
368 	git__free(tag.tag_name);
369 	git__free(tag.message);
370 	git_odb_object_free(target_obj);
371 
372 	/** Ensure the tag name doesn't conflict with an already existing
373 	 *	reference unless overwriting has explicitly been requested **/
374 	if (error == 0 && !allow_ref_overwrite) {
375 		git_error_set(GIT_ERROR_TAG, "tag already exists");
376 		return GIT_EEXISTS;
377 	}
378 
379 	/* write the buffer */
380 	if ((error = git_odb_open_wstream(
381 			&stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
382 		return error;
383 
384 	if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
385 		error = git_odb_stream_finalize_write(oid, stream);
386 
387 	git_odb_stream_free(stream);
388 
389 	if (error < 0) {
390 		git_buf_dispose(&ref_name);
391 		return error;
392 	}
393 
394 	error = git_reference_create(
395 		&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
396 
397 	git_reference_free(new_ref);
398 	git_buf_dispose(&ref_name);
399 
400 	return error;
401 
402 on_error:
403 	git_signature_free(tag.tagger);
404 	git__free(tag.tag_name);
405 	git__free(tag.message);
406 	git_odb_object_free(target_obj);
407 	return -1;
408 }
409 
git_tag_delete(git_repository * repo,const char * tag_name)410 int git_tag_delete(git_repository *repo, const char *tag_name)
411 {
412 	git_reference *tag_ref;
413 	git_buf ref_name = GIT_BUF_INIT;
414 	int error;
415 
416 	error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
417 
418 	git_buf_dispose(&ref_name);
419 
420 	if (error < 0)
421 		return error;
422 
423 	error = git_reference_delete(tag_ref);
424 
425 	git_reference_free(tag_ref);
426 
427 	return error;
428 }
429 
430 typedef struct {
431 	git_repository *repo;
432 	git_tag_foreach_cb cb;
433 	void *cb_data;
434 } tag_cb_data;
435 
tags_cb(const char * ref,void * data)436 static int tags_cb(const char *ref, void *data)
437 {
438 	int error;
439 	git_oid oid;
440 	tag_cb_data *d = (tag_cb_data *)data;
441 
442 	if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
443 		return 0; /* no tag */
444 
445 	if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
446 		if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
447 			git_error_set_after_callback_function(error, "git_tag_foreach");
448 	}
449 
450 	return error;
451 }
452 
git_tag_foreach(git_repository * repo,git_tag_foreach_cb cb,void * cb_data)453 int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
454 {
455 	tag_cb_data data;
456 
457 	assert(repo && cb);
458 
459 	data.cb = cb;
460 	data.cb_data = cb_data;
461 	data.repo = repo;
462 
463 	return git_reference_foreach_name(repo, &tags_cb, &data);
464 }
465 
466 typedef struct {
467 	git_vector *taglist;
468 	const char *pattern;
469 } tag_filter_data;
470 
471 #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
472 
tag_list_cb(const char * tag_name,git_oid * oid,void * data)473 static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
474 {
475 	tag_filter_data *filter = (tag_filter_data *)data;
476 	GIT_UNUSED(oid);
477 
478 	if (!*filter->pattern ||
479 	    wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
480 	{
481 		char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
482 		GIT_ERROR_CHECK_ALLOC(matched);
483 
484 		return git_vector_insert(filter->taglist, matched);
485 	}
486 
487 	return 0;
488 }
489 
git_tag_list_match(git_strarray * tag_names,const char * pattern,git_repository * repo)490 int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
491 {
492 	int error;
493 	tag_filter_data filter;
494 	git_vector taglist;
495 
496 	assert(tag_names && repo && pattern);
497 
498 	if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
499 		return error;
500 
501 	filter.taglist = &taglist;
502 	filter.pattern = pattern;
503 
504 	error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
505 
506 	if (error < 0)
507 		git_vector_free(&taglist);
508 
509 	tag_names->strings =
510 		(char **)git_vector_detach(&tag_names->count, NULL, &taglist);
511 
512 	return 0;
513 }
514 
git_tag_list(git_strarray * tag_names,git_repository * repo)515 int git_tag_list(git_strarray *tag_names, git_repository *repo)
516 {
517 	return git_tag_list_match(tag_names, "", repo);
518 }
519 
git_tag_peel(git_object ** tag_target,const git_tag * tag)520 int git_tag_peel(git_object **tag_target, const git_tag *tag)
521 {
522 	return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
523 }
524 
525 /* Deprecated Functions */
526 
git_tag_create_frombuffer(git_oid * oid,git_repository * repo,const char * buffer,int allow_ref_overwrite)527 int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
528 {
529 	return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
530 }
531