#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "git.h"
#include "macros.h"
#include "misc.h"
git_repo *add_repo(git_repository *repo, git_repo **first, git_repo **last) {
git_repo *new_repo = calloc(1, sizeof(git_repo));
(*last != NULL) ? ((*last)->next = new_repo) : (*first = new_repo);
new_repo->repo = repo;
*last = new_repo;
return new_repo;
}
int find_ignore_file(const char *path) {
DIR *root = opendir(path);
struct dirent entry, *result;
while (readdir_r(root, &entry, &result) == 0 && result != NULL) {
if (entry.d_type == DT_REG && strcasecmp(entry.d_name, "pullreqd-ignore") == 0) {
return 1;
}
}
return 0;
}
void cleanup_linked_list(git_repo *root) {
if (root != NULL) {
git_repo *repo = root;
cleanup_linked_list(root->next);
repo->repo = NULL;
repo->next = NULL;
free(repo);
}
}
void cleanup_git_repos(git_repository **repos) {
for (int i = 0; repos[i] != NULL; i++) {
git_repository_free(repos[i]);
repos[i] = NULL;
}
free(repos);
}
void cleanup_git(git_repository **repos) {
cleanup_git_repos(repos);
git_libgit2_shutdown();
}
pull_request *get_pull_request(int id, const char *root) {
}
int get_info_len(pull_request *pr) {
struct tm tm;
int len = format_len("title: %s\n", pr->title);
localtime_r(&pr->date, &tm);
len += format_len("author: %s\n", pr->author);
len += format_len("date: ") + strftime(NULL, -1, "%c %z\n", &tm);
len += format_len("description: %s\n", pr->desc);
len += format_len("type: %i\n", pr->pr_type);
if (pr->pr_type) {
len += format_len("branch: %s\n", pr->branch->name);
len += format_len("repo: %s", pr->branch->repo);
} else {
len += format_len("patches:");
for (int i = 0; pr->patches[i] != NULL; i++) {
len += format_len(" %s", pr->patches[i]->name);
}
}
return len;
}
int create_info_file(pull_request *pr, const char *pr_root) {
int len;
int buf_len;
int j = 0;
char *filename;
char *file_buf;
FILE *fp;
struct tm tm;
if (pr == NULL) {
log(LOG_ERR,"Pull request is NULL.");
return 0;
}
if (pr_root == NULL) {
log(LOG_ERR,"Pull request root is NULL.");
return 0;
}
localtime_r(&pr->date, &tm);
len = strlen(pr_root) + strlen("/info");
buf_len = get_info_len(pr);
filename = calloc(len+1, sizeof(char));
file_buf = calloc(buf_len+1, sizeof(char));
j = sprintf(file_buf, "title: %s\n", pr->title);
j += sprintf(file_buf+j, "author: %s\n", pr->author);
j += sprintf(file_buf+j, "date: ");
j += strftime(file_buf+j, -1, "%c %z\n", &tm);
j += sprintf(file_buf+j, "description: %s\n", pr->desc);
j += sprintf(file_buf+j, "type: %i\n", pr->pr_type);
if (pr->pr_type) {
j += sprintf(file_buf+j, "branch: %s\n", pr->branch->name);
j += sprintf(file_buf+j, "repo: %s", pr->branch->repo);
} else {
j += sprintf(file_buf+j, "patches:");
for (int i = 0; pr->patches[i] != NULL; i++) {
j += sprintf(file_buf+j, " %s", pr->patches[i]->name);
}
}
sprintf(filename, "%s/info", pr_root);
if (access(filename, F_OK) == 0) {
long size = 0;
char *buf = read_file(filename, &size);
log(LOG_NOTICE, "Info file already exists.");
if (buf != NULL) {
if (strcmp(file_buf, buf) == 0) {
log(LOG_NOTICE, "New info file is the same as the existing one.");
free(filename);
free(file_buf);
free(buf);
return 2;
}
free(buf);
} else {
log(LOG_ERR, "Couldn't open existing info file.");
return 0;
}
}
fp = fopen(filename, "w");
if (fp == NULL) {
log(LOG_ERR, "Failed to open info file.");
return 0;
}
fwrite(file_buf, sizeof(char), buf_len, fp);
fclose(fp);
return 1;
}
int add_comment(comment *comment, const char *pr_root) {
int len;
int buf_len;
int j = 0;
char *filename;
char *file_buf;
FILE *fp;
struct tm tm;
if (comment == NULL) {
log(LOG_ERR, "Comment is NULL.");
return 0;
}
if (pr_root == NULL) {
log(LOG_ERR,"Pull request root is NULL.");
return 0;
}
localtime_r(&comment->date, &tm);
len = strlen(pr_root) + strlen("/comments");
buf_len = get_comment_len(comment);
filename = calloc(len+1, sizeof(char));
file_buf = calloc(buf_len+1, sizeof(char));
j = sprintf(file_buf, "id: %i\n", comment->id);
j += sprintf(file_buf+j, "author: %s\n", comment->author);
j += sprintf(file_buf+j, "date: ");
j += strftime(file_buf+j, -1, "%c %z\n", &tm);
if (comment->reply != NULL) {
localtime_r(&comment->reply->date, &tm);
j += sprintf(file_buf+j, "reply-to: %i\n", comment->reply->id);
j += sprintf(file_buf+j, "reply-author: %s\n", comment->reply->author);
j += sprintf(file_buf+j, "reply-date: ");
j += strftime(file_buf+j, -1, "%c %z\n", &tm);
}
j += sprintf(file_buf+j, "description: %s", comment->desc);
sprintf(filename, "%s/comments", pr_root);
fp = fopen(filename, "a");
if (fp == NULL) {
log(LOG_ERR, "Failed to open comments file.");
return 0;
}
fwrite(file_buf, sizeof(char), buf_len, fp);
fclose(fp);
return 1;
}
int create_pull_request_dir(pull_request *pr, int id, const char *root) {
int ret = id;
struct stat st;
file **commits;
char *pr_dir;
if (pr == NULL) {
log(LOG_ERR, "Pull Request is NULL.");
return -1;
}
if (id < 0 && (pr->title == NULL || pr->title[0] == '\0')) {
log(LOG_ERR, "Negative ID, and either blank, or NULL Pull Request title.");
return -1;
}
pr_dir = (pr->title != NULL && pr->title[0] != '\0') ? dir_path_name(root, sanitize_str(pr->title)) : dir_path_num(root, id);
if (!create_info_file(pr, pr_dir)) {
log(LOG_ERR, "Failed to create info file.");
return -1;
}
if (stat(pr_dir, &st) < -1) {
mkdir(pr_dir, 0755);
}
commits = (pr->pr_type) ? get_branch_commits(pr->branch) : pr->patches;
if (commits != NULL) {
for (int i = 0; commits[i] != NULL; i++) {
FILE *fp;
if (access(commits[i]->name, F_OK) == 0) {
long size = 0;
char *buf = read_file(commits[i]->name, &size);
log(LOG_NOTICE, "Patch file %s already exists.", commits[i]->name);
if (buf != NULL) {
if (strcmp(commits[i]->buf, buf) == 0) {
log(LOG_NOTICE, "Patch file %s is the same as the new one.", commits[i]->name);
free(buf);
continue;
}
free(buf);
} else {
log(LOG_WARNING, "Couldn't open patch file %s.", commits[i]->name);
continue;
}
}
fp = fopen(commits[i]->name, "w");
fwrite(commits[i]->buf, sizeof(char), strlen(commits[i]->buf), fp);
fclose(fp);
}
if (commits != NULL && pr->pr_type) {
free_files(commits);
}
} else {
log(LOG_ERR, "Patch file array is NULL.");
ret = -1;
}
free(pr_dir);
return ret;
}
git_repository **init_git(config *cfg) {
git_repository **repos = NULL;
git_repository *repo = NULL;
git_repo *first = NULL, *last = NULL, *current = NULL;
DIR *root = opendir(cfg->git_root);
struct dirent entry, *result;
int repo_count = 0;
log(LOG_INFO, "Initializing libgit2.");
int ret = git_libgit2_init();
log(LOG_INFO, "Searching \"%s\" for repositories.", cfg->git_root);
while (readdir_r(root, &entry, &result) == 0 && result != NULL) {
if (entry.d_type == DT_DIR) {
if (strcmp(entry.d_name, ".") && strcmp(entry.d_name, "..")) {
char *repo_dir = calloc(strlen(cfg->git_root) + strlen(entry.d_name) + 2, sizeof(char));
sprintf(repo_dir, "%s/%s", cfg->git_root, entry.d_name);
if (!find_ignore_file(repo_dir)) {
if (git_repository_open(&repo, repo_dir)) {
log(LOG_ERR, "Failed to open git repository %s, ignoring.", entry.d_name);
} else {
log(LOG_INFO, "Successfully opened git repository %s", entry.d_name);
current = add_repo(repo, &first, &last);
repo_count++;
}
}
free(repo_dir);
}
}
}
if (repo_count) {
log(LOG_INFO, "Found, and opened %i repositories.", repo_count);
repos = calloc(repo_count+1, sizeof(git_repository *));
int i = 0;
for (git_repo *r = first; r != NULL; r = r->next, i++) {
repos[i] = r->repo;
}
cleanup_linked_list(first);
first = NULL;
last = NULL;
current = NULL;
} else {
log(LOG_ERR, "Couldn't find, and/or open any repositories.");
}
return repos;
}