/* $Id$ */
#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>
+
+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;
+ break;
+ }
+ }
+ char* path = rv_strcat3(SVN_ROOT, "/", repouser);
+ char* cmd[] = {"svnadmin", "create", path, NULL};
+ null_exec(cmd);
+ free(path);
+ FILE* f = fopen(APACHE_AUTHZ, "r+");
+ lockf(fileno(f), F_LOCK, 0);
+
+ fseek(f, 0, SEEK_END);
+
+ fprintf(f, "#%%START %s\n", repouser);
+ fprintf(f, "* = r\n");
+ fprintf(f, "%s = r\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;
+ }
+}
+
+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 && strlen(d + incr + 1) > 0 && d[incr] != 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++;
+ if(phase == 1) goto repeat;
+ free(d);
+ }
+ free(svnpath);
+ return true;
+}
+
+char* rv_read_file(const char* repouser, 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", "cat", 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 NULL;
+ }
+ return d;
+ }
+}
#include "rv_version.h"
#include "rv_auth.h"
#include "rv_db.h"
+#include "rv_repo.h"
#include "../../config.h"
char* desc = NULL;
char* page = NULL;
char* nav = NULL;
+char* grepouser;
extern char* user;
+char* url_escape(const char* input) {
+ const char hex[] = "0123456789ABCDEF";
+ char* r = malloc(1);
+ r[0] = 0;
+ char cbuf[2];
+ cbuf[1] = 0;
+ int i;
+ for(i = 0; input[i] != 0; i++) {
+ if(input[i] == 0x20 || input[i] == 0x22 || input[i] == 0x25 || input[i] == 0x2d || input[i] == 0x2e || input[i] == 0x3c || input[i] == 0x3e || input[i] == 0x5c || input[i] == 0x5e || input[i] == 0x5f || input[i] == 0x60 || input[i] == 0x7b || input[i] == 0x7c || input[i] == 0x7d || input[i] == 0x7e || input[i] == 0x21 || input[i] == 0x23 || input[i] == 0x24 || input[i] == 0x26 || input[i] == 0x27 || input[i] == 0x28 || input[i] == 0x29 || input[i] == 0x2a || input[i] == 0x2b || input[i] == 0x2c || input[i] == 0x2f || input[i] == 0x3a || input[i] == 0x3b || input[i] == 0x3d || input[i] == 0x3f || input[i] == 0x40 || input[i] == 0x5b || input[i] == 0x5d) {
+ add_data(&r, "%");
+ cbuf[0] = hex[(input[i] >> 4) & 0xf];
+ add_data(&r, cbuf);
+ cbuf[0] = hex[input[i] & 0xf];
+ add_data(&r, cbuf);
+ } else {
+ cbuf[0] = input[i];
+ add_data(&r, cbuf);
+ }
+ }
+ return r;
+}
+
+char* html_escape(const char* input) {
+ char* r = malloc(1);
+ r[0] = 0;
+ char cbuf[2];
+ cbuf[1] = 0;
+ int i;
+ for(i = 0; input[i] != 0; i++) {
+ if(input[i] == '<') {
+ add_data(&r, "<");
+ } else if(input[i] == '>') {
+ add_data(&r, ">");
+ } else {
+ cbuf[0] = input[i];
+ add_data(&r, cbuf);
+ }
+ }
+ return r;
+}
+
+char* html_escape_nl_to_br(const char* input) {
+ char* r = malloc(1);
+ r[0] = 0;
+ char cbuf[2];
+ cbuf[1] = 0;
+ int i;
+ for(i = 0; input[i] != 0; i++) {
+ if(input[i] == '<') {
+ add_data(&r, "<");
+ } else if(input[i] == '>') {
+ add_data(&r, ">");
+ } else if(input[i] == '\n') {
+ add_data(&r, "<br>");
+ } else {
+ cbuf[0] = input[i];
+ add_data(&r, cbuf);
+ }
+ }
+ return r;
+}
+
+void list_repo(const char* name, const char* rev) {
+ char* showname = html_escape(name);
+ char* urluser = url_escape(user);
+ char* urlrepo = url_escape(name);
+ add_data(&page, "<tr>");
+ add_data(&page, "<td><a href=\"");
+ add_data(&page, INSTANCE_ROOT);
+ add_data(&page, "/?page=repo&reponame=");
+ add_data(&page, urlrepo);
+ add_data(&page, "&username=");
+ add_data(&page, urluser);
+ add_data(&page, "\">");
+ add_data(&page, showname);
+ add_data(&page, "</a></td>");
+ add_data(&page, "<td>");
+ add_data(&page, rev);
+ add_data(&page, "</td>");
+ add_data(&page, "</tr>");
+ free(showname);
+ free(urluser);
+ free(urlrepo);
+}
+
+int fcounter = 0;
+void list_files(const char* pathname) {
+ if(fcounter == 0) {
+ add_data(&nav, "<li><a href=\"#filelist\">File List</a></li>\n");
+ add_data(&page, "<h2 id=\"filelist\">File List</h2>\n");
+ add_data(&page, "<tr style=\"background-color: #D2E1F6;\"><th>Name</th><th>Size</th></tr>\n");
+ }
+ fcounter++;
+ add_data(&page, "<tr style=\"background-color: #");
+ if((fcounter % 2) == 0) {
+ add_data(&page, "D2E1C0");
+ } else {
+ add_data(&page, "FFFFFF");
+ }
+ char* path = rv_get_query("path");
+ if(path == NULL) path = "/";
+ char* query = rv_strdup("?page=repo&reponame=");
+ char* esc;
+ esc = url_escape(rv_get_query("reponame"));
+ add_data(&query, esc);
+ free(esc);
+ add_data(&query, "&username=");
+ esc = url_escape(user);
+ add_data(&query, esc);
+ free(esc);
+ add_data(&query, "&path=");
+ char* urlpath = rv_strcat(path, pathname);
+ esc = url_escape(urlpath);
+ add_data(&query, esc);
+ free(esc);
+ char* sz = malloc(128);
+ sprintf(sz, "%lld", rv_get_filesize(grepouser, urlpath));
+ add_data(&page, "\"><td><a href=\"");
+ add_data(&page, query);
+ add_data(&page, "\">");
+ add_data(&page, pathname);
+ add_data(&page, "</a></td>\n");
+ add_data(&page, "<td>\n");
+ if(strcmp(sz, "-1") != 0) {
+ add_data(&page, sz);
+ } else {
+ add_data(&page, "<DIR>");
+ }
+ add_data(&page, "</td>\n");
+ free(sz);
+ add_data(&page, "</tr>\n");
+ free(query);
+ free(urlpath);
+}
+
void render_page(void) {
rv_load_query('Q');
char* query = rv_get_query("page");
page = rv_strdup("");
rv_load_query('P');
- if(rv_get_query("username") == NULL || rv_get_query("password") == NULL) {
+ if(user != NULL) {
+ page = rv_strdup("It looks like you are already logged in.<br>Want to <a href=\"");
+ add_data(&page, INSTANCE_ROOT);
+ add_data(&page, "/?page=login\">log out</a>?\n");
+ } else if(rv_get_query("username") == NULL || rv_get_query("password") == NULL) {
add_data(&page, "Invalid form.\n");
} else {
if(rv_has_user(rv_get_query("username"))) {
add_data(&page, INSTANCE_ROOT);
add_data(&page, "/?page=login\">log in</a>?\n");
} else {
+ char cbuf[2];
+ cbuf[0] = REPO_USER_DELIM;
+ cbuf[1] = 0;
nav = rv_strdup("");
add_data(&nav, "<li><a href=\"#createrepo\">Create a repository</a></li>\n");
add_data(&nav, "<li><a href=\"#repolist\">Repository List</a></li>\n");
add_data(&page, " <td><input type=\"submit\" value=\"Create\"></td>\n");
add_data(&page, " </tr>\n");
add_data(&page, " </table>\n");
+ add_data(&page, "Repository name cannot contain '<code>");
+ add_data(&page, cbuf);
+ add_data(&page, "</code>'.");
add_data(&page, "</form>\n");
add_data(&page, "<h2 id=\"repolist\">Repository List</h2>\n");
+ add_data(&page, "<table border=\"0\">\n");
+ add_data(&page, "<tr><th>Repository name</th><th>Revision</th></tr>\n");
+ rv_repo_list(user, list_repo);
+ add_data(&page, "</table>\n");
+ }
+ } else if(strcmp(query, "createrepo") == 0) {
+ title = rv_strdup("Creating Repository Result");
+ page = rv_strdup("");
+
+ rv_load_query('P');
+ if(user == NULL) {
+ page = rv_strdup("It looks like you are not logged in.<br>Want to <a href=\"");
+ add_data(&page, INSTANCE_ROOT);
+ add_data(&page, "/?page=login\">log in</a>?\n");
+ } else if(rv_get_query("name") == NULL) {
+ add_data(&page, "Invalid form.\n");
+ } else {
+ int i;
+ bool reject = false;
+ char* name = rv_get_query("name");
+ for(i = 0; name[i] != 0; i++) {
+ if(name[i] == REPO_USER_DELIM) {
+ char cbuf[2];
+ cbuf[0] = REPO_USER_DELIM;
+ cbuf[1] = 0;
+ add_data(&page, "Repository name cannot contain '<code>");
+ add_data(&page, cbuf);
+ add_data(&page, "</code>'.");
+ reject = true;
+ break;
+ }
+ }
+ if(!reject) {
+ char* ru = rv_construct_repouser(name, user);
+ if(rv_repo_exists(ru)) {
+ add_data(&page, "Repository already exists.");
+ } else {
+ char* esc;
+ rv_create_repo(ru);
+ add_data(&page, "Repository has been created.<br>\n");
+ add_data(&page, "<a href=\"");
+ add_data(&page, INSTANCE_ROOT);
+ esc = url_escape(name);
+ add_data(&page, "/?page=repo&reponame=");
+ add_data(&page, esc);
+ free(esc);
+ esc = url_escape(user);
+ add_data(&page, "&username=");
+ add_data(&page, esc);
+ free(esc);
+ add_data(&page, "\">Go to the repository</a>.\n");
+ }
+ free(ru);
+ }
}
} else if(strcmp(query, "logout") == 0) {
title = rv_strdup("Logout");
add_data(&page, " <input type=\"submit\" value=\"Yes\">\n");
add_data(&page, "</form>\n");
}
+ } else if(strcmp(query, "repo") == 0) {
+ title = rv_strdup("Repository");
+ desc = rv_strdup("");
+ page = rv_strdup("");
+ nav = rv_strdup("");
+ if(rv_get_query("username") == NULL || rv_get_query("reponame") == NULL) {
+ add_data(&page, "Required parameters not set.");
+ } else {
+ char* user = rv_get_query("username");
+ char* repo = rv_get_query("reponame");
+ char* repouser = rv_construct_repouser(repo, user);
+ grepouser = repouser;
+ if(rv_repo_exists(repouser)) {
+ char* showuser = html_escape(user);
+ char* showrepo = html_escape(repo);
+ char* showreadme = rv_get_readme(repouser);
+ desc = html_escape_nl_to_br(showreadme);
+ add_data(&title, " - ");
+ add_data(&title, showrepo);
+ add_data(&title, "/");
+ add_data(&title, showuser);
+ free(showuser);
+ free(showrepo);
+ free(showreadme);
+
+ int isdir;
+ char* path = rv_get_query("path");
+ if(path == NULL) path = "/";
+ fcounter = 0;
+ add_data(&page, "<table border=\"0\" style=\"width: 100%;\">");
+ if(!rv_get_list(repouser, path, list_files, &isdir)) {
+ add_data(&page, "<tr><td>Path not found.</td></tr>\n");
+ }
+ add_data(&page, "</table>");
+ if(isdir == 0) {
+ add_data(&nav, "<li><a href=\"#filecontent\">Content</a></li>");
+ add_data(&page, "<h2 id=\"filecontent\">Content</h2>\n");
+ add_data(&page, "<pre class=\"codeblock\"><code>");
+ char* data = rv_read_file(repouser, path);
+ char* esc = html_escape_nl_to_br(data);
+ add_data(&page, esc);
+ free(esc);
+ free(data);
+ add_data(&page, "</code></pre>");
+ }
+ } else {
+ add_data(&page, "Repository does not exist.\n");
+ }
+ free(repouser);
+ }
}
if(title == NULL) title = rv_strdup("");
add_data(&buffer, " padding-right: 25px;\n");
add_data(&buffer, " padding-top: 7px;\n");
add_data(&buffer, "}\n");
+ add_data(&buffer, "th,td {\n");
+ add_data(&buffer, " padding: 2px;\n");
+ add_data(&buffer, "}\n");
add_data(&buffer, "body {\n");
add_data(&buffer, " background-color: #1F4677;\n");
add_data(&buffer, " width: 940px;\n");
add_data(&buffer, " font-size: 22px;\n");
add_data(&buffer, " font-weight: bold;\n");
add_data(&buffer, "}\n");
+ add_data(&buffer, "pre {\n");
+ add_data(&buffer, " background-color: #dddddd;\n");
+ add_data(&buffer, " border: solid 2px #bbbbbb;\n");
+ add_data(&buffer, "}\n");
add_data(&buffer, "#index {\n");
add_data(&buffer, " list-style: none;\n");
add_data(&buffer, " line-height: normal;\n");
add_data(&buffer, "}\n");
add_data(&buffer, "#descinside {\n");
add_data(&buffer, " float: left;\n");
+ add_data(&buffer, " width: 700px;\n");
+ add_data(&buffer, " overflow-y: scroll;\n");
+ add_data(&buffer, " max-height: 128px;\n");
add_data(&buffer, "}\n");
add_data(&buffer, "#logo {\n");
add_data(&buffer, " float: right;\n");
add_data(&buffer, " background-color: #FFFFFF;\n");
add_data(&buffer, " margin: -10px auto;\n");
add_data(&buffer, " padding: 8px 24px 24px;\n");
- add_data(&buffer, " min-height: 128px;\n");
add_data(&buffer, "}\n");
add_data(&buffer, "#pageindex {\n");
add_data(&buffer, " background-color: #FFFFFF;\n");
add_data(&buffer, " </div>\n");
add_data(&buffer, " <div id=\"pagecontent\">\n");
add_data(&buffer, page);
- add_data(&buffer, " <div class=\"fixfloat\"></div>\n");
add_data(&buffer, " </div>\n");
+ add_data(&buffer, " <div class=\"fixfloat\"></div>\n");
add_data(&buffer, " </div>\n");
add_data(&buffer, " <div id=\"footer\">\n");
add_data(&buffer, " <div id=\"gotop\">\n");