Subversion Repositories RepoView

Rev

Rev 16 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* $Id: repo.c 20 2024-08-21 16:13:13Z nishi $ */

#include "rv_repo.h"

#include "../config.h"

#include "rv_util.h"

#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>

char* rv_construct_repouser(const char* reponame, const char* username) {
        char cbuf[2];
        cbuf[0] = REPO_USER_DELIM;
        cbuf[1] = 0;
        return rv_strcat3(reponame, cbuf, username);
}

bool rv_repo_exists(const char* repouser) {
        char* path = rv_strcat3(SVN_ROOT, "/", repouser);
        if(access(path, F_OK) == 0) {
                free(path);
                return true;
        }
        return false;
}

void rv_repo_list(const char* username, void (*handler)(const char* name, const char* rev)) {
        struct dirent** nl;
        int n = scandir(SVN_ROOT, &nl, NULL, alphasort);
        if(n < 0) return;
        int i;
        for(i = 0; i < n; i++) {
                if(strcmp(nl[i]->d_name, "..") != 0 && strcmp(nl[i]->d_name, ".") != 0) {
                        char* tmp = rv_strcat3(SVN_ROOT, "/", nl[i]->d_name);
                        char* path = rv_strcat(tmp, "/db/current");
                        free(tmp);
                        char* str = rv_strdup(nl[i]->d_name);
                        int j;
                        for(j = 0; str[j] != 0; j++) {
                                if(str[j] == REPO_USER_DELIM) {
                                        str[j] = 0;
                                        if(strcmp(str + j + 1, username) == 0) {
                                                struct stat s;
                                                char* rev = rv_strdup("???");
                                                if(stat(path, &s) == 0) {
                                                        free(rev);
                                                        rev = malloc(s.st_size + 1);
                                                        FILE* f = fopen(path, "r");
                                                        fread(rev, 1, s.st_size, f);
                                                        fclose(f);
                                                        rev[s.st_size] = 0;
                                                }
                                                handler(str, rev);
                                                free(rev);
                                        }
                                        break;
                                }
                        }
                        free(path);
                        free(str);
                }
                free(nl[i]);
        }
        free(nl);
}

void null_exec(char** cmd) {
        pid_t pid = fork();
        if(pid == 0) {
                int null = open("/dev/null", O_RDWR);
                dup2(STDOUT_FILENO, null);
                execvp(cmd[0], cmd);
                _exit(0);
        } else {
                waitpid(pid, 0, 0);
        }
}

void rv_create_repo(const char* repouser) {
        char* user = rv_strdup(repouser);
        int i;
        for(i = 0; user[i] != 0; i++) {
                if(user[i] == REPO_USER_DELIM) {
                        user[i] = 0;
                        char* tmp = user;
                        user = rv_strdup(user + i + 1);
                        free(tmp);
                        break;
                }
        }
        char* path = rv_strcat3(SVN_ROOT, "/", repouser);
        char* cmd[] = {"svnadmin", "create", path, NULL};
        null_exec(cmd);

        char* readme = rv_strcat(path, "/README.txt");
        FILE* f = fopen(readme, "w");
        fprintf(f, "This is the default README of the RepoView repository.\n");
        fprintf(f, "You can change it from the repository manager.\n");
        fclose(f);
        free(readme);

        free(path);
        f = fopen(APACHE_AUTHZ, "r+");
        lockf(fileno(f), F_LOCK, 0);

        fseek(f, 0, SEEK_END);

        fprintf(f, "#%%START %s\n", repouser);
        fprintf(f, "[%s:/]\n", repouser);
        fprintf(f, "* = r\n");
        fprintf(f, "%s = rw\n", user);
        fprintf(f, "#%%END\n");

        lockf(fileno(f), F_ULOCK, 0);
        free(user);
}

char* rv_get_readme(const char* repouser) {
        char* tmp = rv_strcat3(SVN_ROOT, "/", repouser);
        char* path = rv_strcat(tmp, "/README.txt");
        free(tmp);
        struct stat s;
        if(stat(path, &s) == 0) {
                FILE* f = fopen(path, "r");
                char* buf = malloc(s.st_size + 1);
                fread(buf, 1, s.st_size, f);
                fclose(f);
                buf[s.st_size] = 0;
                return buf;
        }
        return NULL;
}

long long rv_get_filesize(const char* repouser, const char* path) {
        char* svnpath = rv_strcat3(SVN_ROOT, "/", repouser);
        int pipes[2];
        pipe(pipes);
        pid_t pid = fork();
        if(pid == 0) {
                close(pipes[0]);
                dup2(pipes[1], STDOUT_FILENO);
                char* cmd[] = {"svnlook", "filesize", svnpath, (char*)path, NULL};
                execvp("svnlook", cmd);
                _exit(0);
        } else {
                close(pipes[1]);
                char cbuf[2];
                cbuf[1] = 0;
                char* d = malloc(1);
                d[0] = 0;
                while(1) {
                        int n = read(pipes[0], cbuf, 1);
                        if(n == 0) break;
                        char* tmp = d;
                        d = rv_strcat(tmp, cbuf);
                        free(tmp);
                }
                int status;
                waitpid(pid, &status, 0);
                if(WEXITSTATUS(status) != 0) {
                        free(d);
                        free(svnpath);
                        return -1;
                }
                long long sz = atoll(d);
                free(svnpath);
                free(d);
                return sz;
        }
}

void rv_remove_repo(const char* repouser) {
        printf("");
        char* svnpath = rv_strcat3(SVN_ROOT, "/", repouser);
        pid_t pid = fork();
        if(pid == 0) {
                char* cmd[] = {"rm", "-rf", svnpath, NULL};
                execvp("rm", cmd);
                _exit(0);
        } else {
                waitpid(pid, 0, 0);
        }
        free(svnpath);

        FILE* f = fopen(APACHE_AUTHZ, "r+");
        lockf(fileno(f), F_LOCK, 0);

        fseek(f, 0, SEEK_SET);
        struct stat s;
        stat(APACHE_AUTHZ, &s);
        char* buffer = malloc(s.st_size + 1);
        fread(buffer, 1, s.st_size, f);
        buffer[s.st_size] = 0;

        f = freopen(APACHE_AUTHZ, "w+", f);
        lockf(fileno(f), F_LOCK, 0);
        int incr = 0;
        int i;
        char* start = rv_strcat("#%START ", repouser);
        bool discard = false;
        for(i = 0;; i++) {
                if(buffer[i] == '\n' || buffer[i] == 0) {
                        char oldc = buffer[i];
                        buffer[i] = 0;

                        char* line = buffer + incr;
                        if(strcmp(line, start) == 0) {
                                discard = true;
                        } else if(discard && strcmp(line, "#%END") == 0) {
                                discard = false;
                        } else if(strcmp(line, "") == 0) {
                        } else if(!discard) {
                                fprintf(f, "%s\n", line);
                        }

                        incr = i + 1;
                        if(oldc == 0) break;
                }
        }

        lockf(fileno(f), F_ULOCK, 0);
        fclose(f);
}

void rv_set_readme(const char* repouser, const char* readme) {
        char* svnpath = rv_strcat3(SVN_ROOT, "/", repouser);
        char* path = rv_strcat(svnpath, "/README.txt");
        FILE* f = fopen(path, "w");
        if(f != NULL) {
                fwrite(readme, 1, strlen(readme), f);
                fclose(f);
        }
        free(path);
        free(svnpath);
}

bool rv_get_list(const char* repouser, const char* path, void (*handler)(const char* pathname), int* isdir) {
        char* svnpath = rv_strcat3(SVN_ROOT, "/", repouser);
        int pipes[2];
        *isdir = 0;
        pipe(pipes);
        pid_t pid = fork();
        if(pid == 0) {
                close(pipes[0]);
                dup2(pipes[1], STDOUT_FILENO);
                char* cmd[] = {"svnlook", "-N", "tree", svnpath, (char*)path, NULL};
                execvp("svnlook", cmd);
                _exit(0);
        } else {
                close(pipes[1]);
                char cbuf[2];
                cbuf[1] = 0;
                char* d = malloc(1);
                d[0] = 0;
                while(1) {
                        int n = read(pipes[0], cbuf, 1);
                        if(n == 0) break;
                        char* tmp = d;
                        d = rv_strcat(tmp, cbuf);
                        free(tmp);
                }
                int status;
                waitpid(pid, &status, 0);
                if(WEXITSTATUS(status) != 0) {
                        free(d);
                        free(svnpath);
                        return false;
                }
                int count = 0;
                int incr = 0;
                int i;
                int phase = 0;
        repeat:
                for(i = 0;; i++) {
                        if(d[i] == '\r') {
                                d[i] = 0;
                        } else if(d[i] == '\n' || d[i] == 0) {
                                char oldc = d[i];
                                d[i] = 0;
                                count++;
                                if(count > 1 && d[incr] != 0 && strlen(d + incr + 1) > 0) {
                                        char* pathname = d + incr + 1;
                                        if(phase == 0 && pathname[strlen(pathname) - 1] == '/') {
                                                handler(d + incr + 1);
                                        } else if(phase == 1 && pathname[strlen(pathname) - 1] != '/') {
                                                handler(d + incr + 1);
                                        }
                                } else {
                                        char* pathname = d + incr;
                                        if(pathname[strlen(pathname) - 1] == '/') *isdir = 1;
                                }
                                d[i] = oldc;
                                incr = i + 1;
                                if(oldc == 0) break;
                        }
                }
                phase++;
                incr = 0;
                count = 0;
                if(phase == 1) goto repeat;
                free(d);
        }
        free(svnpath);
        return true;
}

char* rv_read_file(const char* repouser, char* path) {
        if(rv_get_filesize(repouser, path) > 1024 * 128) return NULL;
        char* svnpath = rv_strcat3(SVN_ROOT, "/", repouser);
        int pipes[2];
        pipe(pipes);
        pid_t pid = fork();
        if(pid == 0) {
                close(pipes[0]);
                dup2(pipes[1], STDOUT_FILENO);
                char* cmd[] = {"svnlook", "cat", svnpath, (char*)path, NULL};
                execvp("svnlook", cmd);
                _exit(0);
        } else {
                close(pipes[1]);
                char cbuf[1024];
                char* d = malloc(1024 * 128);
                int incr = 0;
                bool bin = false;
                while(1) {
                        int n = read(pipes[0], cbuf, 1024);
                        if(cbuf[0] == 0) {
                                bin = true;
                                kill(pid, SIGKILL);
                                break;
                        }
                        if(n == 0) break;
                        memcpy(d + incr, cbuf, n);
                        d[incr + n] = 0;
                        incr += n;
                }
                int status;
                waitpid(pid, &status, 0);
                if(WEXITSTATUS(status) != 0) {
                        free(d);
                        free(svnpath);
                        return NULL;
                }
                if(bin) {
                        free(d);
                        return NULL;
                }
                return d;
        }
}