int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port) { char *colon, *slash, *delim; colon = strchr(url, ':'); slash = strchr(url, '/'); if (slash == NULL) { giterr_set(GITERR_NET, "Malformed URL: missing /"); return -1; } if (colon == NULL) { *port = git__strdup(default_port); } else { *port = git__strndup(colon + 1, slash - colon - 1); } GITERR_CHECK_ALLOC(*port); delim = colon == NULL ? slash : colon; *host = git__strndup(url, delim - url); GITERR_CHECK_ALLOC(*host); return 0; }
int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port) { char *colon, *slash, *delim; int error = GIT_SUCCESS; colon = strchr(url, ':'); slash = strchr(url, '/'); if (slash == NULL) return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /"); if (colon == NULL) { *port = git__strdup(default_port); } else { *port = git__strndup(colon + 1, slash - colon - 1); } if (*port == NULL) return GIT_ENOMEM;; delim = colon == NULL ? slash : colon; *host = git__strndup(url, delim - url); if (*host == NULL) { git__free(*port); error = GIT_ENOMEM; } return error; }
static int parse_name( char **name, const char **value, git_config_parser *reader, const char *line) { const char *name_end = line, *value_start; *name = NULL; *value = NULL; while (*name_end && is_namechar(*name_end)) name_end++; if (line == name_end) { set_parse_error(reader, 0, "Invalid configuration key"); return -1; } value_start = name_end; while (*value_start && git__isspace(*value_start)) value_start++; if (*value_start == '=') { *value = value_start + 1; } else if (*value_start) { set_parse_error(reader, 0, "Invalid configuration key"); return -1; } if ((*name = git__strndup(line, name_end - line)) == NULL) return -1; return 0; }
int p_lstat(const char *file_name, struct stat *buf) { int error; size_t namelen; char *alt_name; if (do_lstat(file_name, buf) == 0) return 0; /* if file_name ended in a '/', Windows returned ENOENT; * try again without trailing slashes */ namelen = strlen(file_name); if (namelen && file_name[namelen-1] != '/') return -1; while (namelen && file_name[namelen-1] == '/') --namelen; if (!namelen) return -1; alt_name = git__strndup(file_name, namelen); if (!alt_name) return -1; error = do_lstat(alt_name, buf); git__free(alt_name); return error; }
int git_refspec_parse(git_refspec *refspec, const char *str) { char *delim; memset(refspec, 0x0, sizeof(git_refspec)); if (*str == '+') { refspec->force = 1; str++; } delim = strchr(str, ':'); if (delim == NULL) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'"); refspec->src = git__strndup(str, delim - str); if (refspec->src == NULL) return GIT_ENOMEM; refspec->dst = git__strdup(delim + 1); if (refspec->dst == NULL) { free(refspec->src); refspec->src = NULL; return GIT_ENOMEM; } return GIT_SUCCESS; }
const char *git_commit_body(git_commit *commit) { const char *msg, *end; assert(commit); if (!commit->body) { /* search for end of summary */ for (msg = git_commit_message(commit); *msg; ++msg) if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) break; /* trim leading and trailing whitespace */ for (; *msg; ++msg) if (!git__isspace(*msg)) break; for (end = msg + strlen(msg) - 1; msg <= end; --end) if (!git__isspace(*end)) break; if (*msg) commit->body = git__strndup(msg, end - msg + 1); } return commit->body; }
static int parse_variable(git_config_parser *reader, char **var_name, char **var_value) { const char *value_start = NULL; char *line; int quote_count; bool multiline; git_parse_advance_ws(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); if (line == NULL) return -1; quote_count = strip_comments(line, 0); /* If there is no value, boolean true is assumed */ *var_value = NULL; if (parse_name(var_name, &value_start, reader, line) < 0) goto on_error; /* * Now, let's try to parse the value */ if (value_start != NULL) { while (git__isspace(value_start[0])) value_start++; if (unescape_line(var_value, &multiline, value_start, 0) < 0) goto on_error; if (multiline) { git_buf multi_value = GIT_BUF_INIT; git_buf_attach(&multi_value, *var_value, 0); if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) { git_buf_free(&multi_value); goto on_error; } *var_value = git_buf_detach(&multi_value); } } git__free(line); return 0; on_error: git__free(*var_name); git__free(line); return -1; }
static int find_by_path(const git_config_entry *entry, void *payload) { fbp_data *data = payload; if (!strcmp(entry->value, data->path)) { const char *fdot, *ldot; fdot = strchr(entry->name, '.'); ldot = strrchr(entry->name, '.'); data->name = git__strndup(fdot + 1, ldot - fdot - 1); GITERR_CHECK_ALLOC(data->name); } return 0; }
static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes) { char *line = NULL, *proc_line = NULL; int quote_count; bool multiline; /* Check that the next line exists */ git_parse_advance_line(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); if (line == NULL) return -1; /* We've reached the end of the file, there is no continuation. * (this is not an error). */ if (line[0] == '\0') { git__free(line); return 0; } quote_count = strip_comments(line, !!in_quotes); /* If it was just a comment, pretend it didn't exist */ if (line[0] == '\0') { git__free(line); return parse_multiline_variable(reader, value, quote_count); /* TODO: unbounded recursion. This **could** be exploitable */ } if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) { git__free(line); return -1; } /* add this line to the multiline var */ git_buf_puts(value, proc_line); git__free(line); git__free(proc_line); /* * If we need to continue reading the next line, let's just * keep putting stuff in the buffer */ if (multiline) return parse_multiline_variable(reader, value, quote_count); return 0; }
static int check_prefix( char **out, size_t *out_len, git_patch_parsed *patch, const char *path_start) { const char *path = path_start; size_t prefix_len = patch->ctx->opts.prefix_len; size_t remain_len = prefix_len; *out = NULL; *out_len = 0; if (prefix_len == 0) goto done; /* leading slashes do not count as part of the prefix in git apply */ while (*path == '/') path++; while (*path && remain_len) { if (*path == '/') remain_len--; path++; } if (remain_len || !*path) return git_parse_err( "header filename does not contain %"PRIuZ" path components", prefix_len); done: *out_len = (path - path_start); *out = git__strndup(path_start, *out_len); return (*out == NULL) ? -1 : 0; }
static int note_new( git_note **out, git_oid *note_oid, git_commit *commit, git_blob *blob) { git_note *note = NULL; note = (git_note *)git__malloc(sizeof(git_note)); GITERR_CHECK_ALLOC(note); git_oid_cpy(¬e->id, note_oid); if (git_signature_dup(¬e->author, git_commit_author(commit)) < 0 || git_signature_dup(¬e->committer, git_commit_committer(commit)) < 0) return -1; note->message = git__strndup(git_blob_rawcontent(blob), git_blob_rawsize(blob)); GITERR_CHECK_ALLOC(note->message); *out = note; return 0; }
int git_path_dirload( git_vector *contents, const char *path, size_t prefix_len, unsigned int flags) { git_path_diriter iter = GIT_PATH_DIRITER_INIT; const char *name; size_t name_len; char *dup; int error; assert(contents && path); if ((error = git_path_diriter_init(&iter, path, flags)) < 0) return error; while ((error = git_path_diriter_next(&iter)) == 0) { if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0) break; assert(name_len > prefix_len); dup = git__strndup(name + prefix_len, name_len - prefix_len); GITERR_CHECK_ALLOC(dup); if ((error = git_vector_insert(contents, dup)) < 0) break; } if (error == GIT_ITEROVER) error = 0; git_path_diriter_free(&iter); return error; }
int git_commit__parse_raw(void *_commit, const char *data, size_t size) { git_commit *commit = _commit; const char *buffer_start = data, *buffer; const char *buffer_end = buffer_start + size; git_oid parent_id; size_t header_len; git_signature dummy_sig; buffer = buffer_start; /* Allocate for one, which will allow not to realloc 90% of the time */ git_array_init_to_size(commit->parent_ids, 1); GITERR_CHECK_ARRAY(commit->parent_ids); /* The tree is always the first field */ if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) goto bad_buffer; /* * TODO: commit grafts! */ while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) { git_oid *new_id = git_array_alloc(commit->parent_ids); GITERR_CHECK_ALLOC(new_id); git_oid_cpy(new_id, &parent_id); } commit->author = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(commit->author); if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) return -1; /* Some tools create multiple author fields, ignore the extra ones */ while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) { if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0) return -1; git__free(dummy_sig.name); git__free(dummy_sig.email); } /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(commit->committer); if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0) return -1; /* Parse add'l header entries */ while (buffer < buffer_end) { const char *eoln = buffer; if (buffer[-1] == '\n' && buffer[0] == '\n') break; while (eoln < buffer_end && *eoln != '\n') ++eoln; if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) { buffer += strlen("encoding "); commit->message_encoding = git__strndup(buffer, eoln - buffer); GITERR_CHECK_ALLOC(commit->message_encoding); } if (eoln < buffer_end && *eoln == '\n') ++eoln; buffer = eoln; } header_len = buffer - buffer_start; commit->raw_header = git__strndup(buffer_start, header_len); GITERR_CHECK_ALLOC(commit->raw_header); /* point "buffer" to data after header, +1 for the final LF */ buffer = buffer_start + header_len + 1; /* extract commit message */ if (buffer <= buffer_end) commit->raw_message = git__strndup(buffer, buffer_end - buffer); else commit->raw_message = git__strdup(""); GITERR_CHECK_ALLOC(commit->raw_message); return 0; bad_buffer: giterr_set(GITERR_OBJECT, "failed to parse bad commit object"); return -1; }
int gitno_connection_data_from_url( gitno_connection_data *data, const char *url, const char *service_suffix) { int error = -1; const char *default_port = NULL, *path_search_start = NULL; char *original_host = NULL; /* service_suffix is optional */ assert(data && url); /* Save these for comparison later */ original_host = data->host; data->host = NULL; gitno_connection_data_free_ptrs(data); if (!git__prefixcmp(url, prefix_http)) { path_search_start = url + strlen(prefix_http); default_port = "80"; if (data->use_ssl) { giterr_set(GITERR_NET, "redirect from HTTPS to HTTP is not allowed"); goto cleanup; } } else if (!git__prefixcmp(url, prefix_https)) { path_search_start = url + strlen(prefix_https); default_port = "443"; data->use_ssl = true; } else if (url[0] == '/') default_port = data->use_ssl ? "443" : "80"; if (!default_port) { giterr_set(GITERR_NET, "unrecognized URL prefix"); goto cleanup; } error = gitno_extract_url_parts( &data->host, &data->port, &data->path, &data->user, &data->pass, url, default_port); if (url[0] == '/') { /* Relative redirect; reuse original host name and port */ path_search_start = url; git__free(data->host); data->host = original_host; original_host = NULL; } if (!error) { const char *path = strchr(path_search_start, '/'); size_t pathlen = strlen(path); size_t suffixlen = service_suffix ? strlen(service_suffix) : 0; if (suffixlen && !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) { git__free(data->path); data->path = git__strndup(path, pathlen - suffixlen); } else { git__free(data->path); data->path = git__strdup(path); } /* Check for errors in the resulting data */ if (original_host && url[0] != '/' && strcmp(original_host, data->host)) { giterr_set(GITERR_NET, "cross host redirect not allowed"); error = -1; } } cleanup: if (original_host) git__free(original_host); return error; }
int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) { // Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636 size_t llen; int is_glob = 0; const char *lhs, *rhs; int flags; assert(refspec && input); memset(refspec, 0x0, sizeof(git_refspec)); refspec->push = !is_fetch; lhs = input; if (*lhs == '+') { refspec->force = 1; lhs++; } rhs = strrchr(lhs, ':'); /* * Before going on, special case ":" (or "+:") as a refspec * for matching refs. */ if (!is_fetch && rhs == lhs && rhs[1] == '\0') { refspec->matching = 1; return 0; } if (rhs) { size_t rlen = strlen(++rhs); is_glob = (1 <= rlen && strchr(rhs, '*')); refspec->dst = git__strndup(rhs, rlen); } llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs)); if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && is_fetch)) goto invalid; is_glob = 1; } else if (rhs && is_glob) goto invalid; refspec->pattern = is_glob; refspec->src = git__strndup(lhs, llen); flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND | (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0); if (is_fetch) { /* * LHS * - empty is allowed; it means HEAD. * - otherwise it must be a valid looking ref. */ if (!*refspec->src) ; /* empty is ok */ else if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; /* * RHS * - missing is ok, and is same as empty. * - empty is ok; it means not to store. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) ; /* ok */ else if (!*refspec->dst) ; /* ok */ else if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } else { /* * LHS * - empty is allowed; it means delete. * - when wildcarded, it must be a valid looking ref. * - otherwise, it must be an extended SHA-1, but * there is no existing way to validate this. */ if (!*refspec->src) ; /* empty is ok */ else if (is_glob) { if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; } else { ; /* anything goes, for now */ } /* * RHS * - missing is allowed, but LHS then must be a * valid looking ref. * - empty is not allowed. * - otherwise it must be a valid looking ref. */ if (!refspec->dst) { if (!git_reference__is_valid_name(refspec->src, flags)) goto invalid; } else if (!*refspec->dst) { goto invalid; } else { if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } /* if the RHS is empty, then it's a copy of the LHS */ if (!refspec->dst) { refspec->dst = git__strdup(refspec->src); GITERR_CHECK_ALLOC(refspec->dst); } } refspec->string = git__strdup(input); GITERR_CHECK_ALLOC(refspec->string); return 0; invalid: giterr_set( GITERR_INVALID, "'%s' is not a valid refspec.", input); return -1; }
static int parse_section_header(git_config_parser *reader, char **section_out) { char *name, *name_end; int name_length, c, pos; int result; char *line; size_t line_len; git_parse_advance_ws(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); if (line == NULL) return -1; /* find the end of the variable's name */ name_end = strrchr(line, ']'); if (name_end == NULL) { git__free(line); set_parse_error(reader, 0, "Missing ']' in section header"); return -1; } GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1); name = git__malloc(line_len); GITERR_CHECK_ALLOC(name); name_length = 0; pos = 0; /* Make sure we were given a section header */ c = line[pos++]; assert(c == '['); c = line[pos++]; do { if (git__isspace(c)){ name[name_length] = '\0'; result = parse_section_header_ext(reader, line, name, section_out); git__free(line); git__free(name); return result; } if (!config_keychar(c) && c != '.') { set_parse_error(reader, pos, "Unexpected character in header"); goto fail_parse; } name[name_length++] = (char)git__tolower(c); } while ((c = line[pos++]) != ']'); if (line[pos - 1] != ']') { set_parse_error(reader, pos, "Unexpected end of file"); goto fail_parse; } git__free(line); name[name_length] = 0; *section_out = name; return 0; fail_parse: git__free(line); git__free(name); return -1; }
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) { const char *buffer = data; const char *buffer_end = (const char *)data + len; git_oid parent_oid; int error; git_vector_init(&commit->parent_oids, 4, NULL); if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) return git__rethrow(error, "Failed to parse buffer"); /* * TODO: commit grafts! */ while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) { git_oid *new_oid; new_oid = git__malloc(sizeof(git_oid)); git_oid_cpy(new_oid, &parent_oid); if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS) return GIT_ENOMEM; } commit->author = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS) return git__rethrow(error, "Failed to parse commit"); /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS) return git__rethrow(error, "Failed to parse commit"); if (git__prefixcmp(buffer, "encoding ") == 0) { const char *encoding_end; buffer += strlen("encoding "); encoding_end = buffer; while (encoding_end < buffer_end && *encoding_end != '\n') encoding_end++; commit->message_encoding = git__strndup(buffer, encoding_end - buffer); if (!commit->message_encoding) return GIT_ENOMEM; buffer = encoding_end; } /* parse commit message */ while (buffer < buffer_end && *buffer == '\n') buffer++; if (buffer < buffer_end) { commit->message = git__strndup(buffer, buffer_end - buffer); if (!commit->message) return GIT_ENOMEM; } return GIT_SUCCESS; }
static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsigned int start) { git_treebuilder *bld = NULL; unsigned int i, entries = git_index_entrycount(index); int error; size_t dirname_len = strlen(dirname); const git_tree_cache *cache; cache = git_tree_cache_get(index->tree, dirname); if (cache != NULL && cache->entries >= 0){ git_oid_cpy(oid, &cache->oid); return find_next_dir(dirname, index, start); } error = git_treebuilder_create(&bld, NULL); if (bld == NULL) { return GIT_ENOMEM; } /* * This loop is unfortunate, but necessary. The index doesn't have * any directores, so we need to handle that manually, and we * need to keep track of the current position. */ for (i = start; i < entries; ++i) { git_index_entry *entry = git_index_get(index, i); char *filename, *next_slash; /* * If we've left our (sub)tree, exit the loop and return. The * first check is an early out (and security for the * third). The second check is a simple prefix comparison. The * third check catches situations where there is a directory * win32/sys and a file win32mmap.c. Without it, the following * code believes there is a file win32/mmap.c */ if (strlen(entry->path) < dirname_len || memcmp(entry->path, dirname, dirname_len) || (dirname_len > 0 && entry->path[dirname_len] != '/')) { break; } filename = entry->path + dirname_len; if (*filename == '/') filename++; next_slash = strchr(filename, '/'); if (next_slash) { git_oid sub_oid; int written; char *subdir, *last_comp; subdir = git__strndup(entry->path, next_slash - entry->path); if (subdir == NULL) { error = GIT_ENOMEM; goto cleanup; } /* Write out the subtree */ written = write_tree(&sub_oid, index, subdir, i); if (written < 0) { error = git__rethrow(written, "Failed to write subtree %s", subdir); } else { i = written - 1; /* -1 because of the loop increment */ } /* * We need to figure out what we want toinsert * into this tree. If we're traversing * deps/zlib/, then we only want to write * 'zlib' into the tree. */ last_comp = strrchr(subdir, '/'); if (last_comp) { last_comp++; /* Get rid of the '/' */ } else { last_comp = subdir; } error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); free(subdir); if (error < GIT_SUCCESS) { error = git__rethrow(error, "Failed to insert dir"); goto cleanup; } } else { error = append_entry(bld, filename, &entry->oid, entry->mode); if (error < GIT_SUCCESS) { error = git__rethrow(error, "Failed to insert file"); } } } error = git_treebuilder_write(oid, index->repository, bld); if (error < GIT_SUCCESS) error = git__rethrow(error, "Failed to write tree to db"); cleanup: git_treebuilder_free(bld); if (error < GIT_SUCCESS) return error; else return i; }
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) { const char *buffer = data; const char *buffer_end = (const char *)data + len; git_oid parent_id; if (git_vector_init(&commit->parent_ids, 4, NULL) < 0) return -1; if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) goto bad_buffer; /* * TODO: commit grafts! */ while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) { git_oid *new_id = git__malloc(sizeof(git_oid)); GITERR_CHECK_ALLOC(new_id); git_oid_cpy(new_id, &parent_id); if (git_vector_insert(&commit->parent_ids, new_id) < 0) return -1; } commit->author = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(commit->author); if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) return -1; /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(commit->committer); if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0) return -1; /* Parse add'l header entries until blank line found */ while (buffer < buffer_end && *buffer != '\n') { const char *eoln = buffer; while (eoln < buffer_end && *eoln != '\n') ++eoln; if (git__prefixcmp(buffer, "encoding ") == 0) { buffer += strlen("encoding "); commit->message_encoding = git__strndup(buffer, eoln - buffer); GITERR_CHECK_ALLOC(commit->message_encoding); } if (eoln < buffer_end && *eoln == '\n') ++eoln; buffer = eoln; } /* skip blank lines */ while (buffer < buffer_end - 1 && *buffer == '\n') buffer++; /* parse commit message */ if (buffer <= buffer_end) { commit->message = git__strndup(buffer, buffer_end - buffer); GITERR_CHECK_ALLOC(commit->message); } return 0; bad_buffer: giterr_set(GITERR_OBJECT, "Failed to parse bad commit object"); return -1; }