aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2016-10-07 01:48:16 +0200
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2016-10-07 01:48:16 +0200
commit2c1b2eaaef8726c71648b4a115fbe628f28d6b4b (patch)
tree3d34829130ee8870e268d3fcd19d4149e66b1be6 /src
parent13be5bd50238daa2be6b42104ba20aeea427655b (diff)
downloadblogc-2c1b2eaaef8726c71648b4a115fbe628f28d6b4b.tar.gz
blogc-2c1b2eaaef8726c71648b4a115fbe628f28d6b4b.tar.bz2
blogc-2c1b2eaaef8726c71648b4a115fbe628f28d6b4b.zip
git-receiver: splitted/reimplemented and tested shell command parser
Diffstat (limited to 'src')
-rw-r--r--src/blogc-git-receiver/shell-command-parser.c125
-rw-r--r--src/blogc-git-receiver/shell-command-parser.h15
-rw-r--r--src/blogc-git-receiver/shell.c97
-rw-r--r--src/blogc-git-receiver/shell.h1
4 files changed, 182 insertions, 56 deletions
diff --git a/src/blogc-git-receiver/shell-command-parser.c b/src/blogc-git-receiver/shell-command-parser.c
new file mode 100644
index 0000000..cc8c537
--- /dev/null
+++ b/src/blogc-git-receiver/shell-command-parser.c
@@ -0,0 +1,125 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "../common/utils.h"
+#include "shell-command-parser.h"
+
+typedef enum {
+ START_COMMAND = 1,
+ START_REPO,
+ START_REPO2,
+ REPO,
+ START_ESCAPED,
+} command_state_t;
+
+
+char*
+bgr_shell_command_parse(const char *command)
+{
+ command_state_t state = START_COMMAND;
+ size_t start = 0;
+ size_t command_len = strlen(command);
+
+ bc_string_t *rv = bc_string_new();
+
+ for (size_t current = 0; current < command_len; current++) {
+
+ char c = command[current];
+
+ switch (state) {
+ case START_COMMAND:
+ if (c == ' ') {
+ if (((current == 16) &&
+ (0 == strncmp("git-receive-pack", command, 16))) ||
+ ((current == 15) &&
+ (0 == strncmp("git-upload-pack", command, 15))) ||
+ ((current == 18) &&
+ (0 == strncmp("git-upload-archive", command, 18))))
+ {
+ state = START_REPO;
+ break;
+ }
+ goto error;
+ }
+ break;
+
+ case START_REPO:
+ if (c == '\'') { // never saw git using double-quotes
+ state = START_REPO2;
+ break;
+ }
+ if (c == '\\') { // escaped ! or '
+ state = START_ESCAPED;
+ break;
+ }
+ goto error;
+
+ case START_REPO2:
+ if (c == '\'') {
+ state = START_REPO;
+ break;
+ }
+ start = current;
+ if (rv->len == 0 && c == '/') { // no absolute urls
+ start = current + 1;
+ }
+ state = REPO;
+ break;
+
+ case START_ESCAPED:
+ if (c == '!' || c == '\'') {
+ bc_string_append_c(rv, c);
+ state = START_REPO;
+ break;
+ }
+ goto error;
+
+ case REPO:
+ if (c == '\'') {
+ bc_string_append_len(rv, command + start, current - start);
+ state = START_REPO;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (rv->len > 0)
+ return bc_string_free(rv, false);
+
+error:
+ bc_string_free(rv, true);
+ return NULL;
+}
+
+
+char*
+bgr_shell_quote(const char *command)
+{
+ // this does not really belongs here, but function is very small
+ bc_string_t *rv = bc_string_new();
+ bc_string_append_c(rv, '\'');
+ if (command != NULL) {
+ for (size_t i = 0; i < strlen(command); i++) {
+ switch (command[i]) {
+ case '!':
+ bc_string_append(rv, "'\\!'");
+ break;
+ case '\'':
+ bc_string_append(rv, "'\\''");
+ break;
+ default:
+ bc_string_append_c(rv, command[i]);
+ }
+ }
+ }
+ bc_string_append_c(rv, '\'');
+ return bc_string_free(rv, false);
+}
diff --git a/src/blogc-git-receiver/shell-command-parser.h b/src/blogc-git-receiver/shell-command-parser.h
new file mode 100644
index 0000000..47054cb
--- /dev/null
+++ b/src/blogc-git-receiver/shell-command-parser.h
@@ -0,0 +1,15 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _SHELL_COMMAND_PARSER_H
+#define _SHELL_COMMAND_PARSER_H
+
+char* bgr_shell_command_parse(const char *command);
+char* bgr_shell_quote(const char *command);
+
+#endif /* _SHELL_COMMAND_PARSER_H */
diff --git a/src/blogc-git-receiver/shell.c b/src/blogc-git-receiver/shell.c
index 290526b..581ec3d 100644
--- a/src/blogc-git-receiver/shell.c
+++ b/src/blogc-git-receiver/shell.c
@@ -16,12 +16,9 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "../common/utils.h"
+#include "shell-command-parser.h"
#include "shell.h"
-#ifndef BUFFER_SIZE
-#define BUFFER_SIZE 4096
-#endif
-
int
bgr_shell(int argc, char *argv[])
@@ -29,22 +26,7 @@ bgr_shell(int argc, char *argv[])
int rv = 0;
char *repo = NULL;
- char *command_orig = NULL;
- char *command_name = NULL;
- char command_new[BUFFER_SIZE];
-
- bool exec_git = false;
-
- // validate git command
- size_t len = strlen(argv[2]);
- if (!((len > 17 && (0 == strncmp(argv[2], "git-receive-pack ", 17))) ||
- (len > 16 && (0 == strncmp(argv[2], "git-upload-pack ", 16))) ||
- (len > 19 && (0 == strncmp(argv[2], "git-upload-archive ", 19)))))
- {
- fprintf(stderr, "error: unsupported git command: %s\n", argv[2]);
- rv = 1;
- goto cleanup;
- }
+ char *quoted_repo = NULL;
// get shell path
char *self = getenv("SHELL");
@@ -62,23 +44,17 @@ bgr_shell(int argc, char *argv[])
goto cleanup;
}
- // get git repository
- command_orig = bc_strdup(argv[2]);
- char *p, *r;
- for (p = command_orig; *p != ' ' && *p != '\0'; p++);
- if (*p == ' ')
- p++;
- if (*p == '\'' || *p == '"')
- p++;
- if (*p == '/')
- p++;
- for (r = p; *p != '\'' && *p != '"' && *p != '\0'; p++);
- if (*p == '\'' || *p == '"')
- *p = '\0';
- if (*--p == '/')
- *p = '\0';
-
- repo = bc_strdup_printf("repos/%s", r);
+ // validate command and extract git repository
+ char *tmp_repo = bgr_shell_command_parse(argv[2]);
+ if (tmp_repo == NULL) {
+ fprintf(stderr, "error: invalid git-shell command: %s\n", argv[2]);
+ rv = 1;
+ goto cleanup;
+ }
+
+ repo = bc_strdup_printf("repos/%s", tmp_repo);
+ quoted_repo = bgr_shell_quote(repo);
+ free(tmp_repo);
// check if repository is sane
if (0 == strlen(repo)) {
@@ -99,7 +75,7 @@ bgr_shell(int argc, char *argv[])
if (0 != access(repo, F_OK)) {
char *git_init_cmd = bc_strdup_printf(
- "git init --bare \"%s\" > /dev/null", repo);
+ "git init --bare %s > /dev/null", quoted_repo);
if (0 != system(git_init_cmd)) {
fprintf(stderr, "error: failed to create git repository: %s\n",
repo);
@@ -175,32 +151,41 @@ bgr_shell(int argc, char *argv[])
}
git_exec:
- command_name = bc_strdup(argv[2]);
- for (p = command_name; *p != ' ' && *p != '\0'; p++);
- if (*p == ' ')
- *p = '\0';
- if (BUFFER_SIZE < (strlen(command_name) + strlen(repo) + 4)) {
- fprintf(stderr, "error: git-shell command is too big\n");
- rv = 1;
- goto cleanup;
- }
+ {
+ // static allocation instead of bc_strdup_printf to avoid leaks
+ char buffer[4096];
+ char *command = bc_strdup(argv[2]);
+ char *p;
+ for (p = command; *p != ' ' && *p != '\0'; p++);
+ if (*p == ' ')
+ *p = '\0';
+
+ if (sizeof(buffer) < (strlen(command) + strlen(quoted_repo) + 2)) {
+ fprintf(stderr, "error: git-shell command is too big\n");
+ rv = 1;
+ goto cleanup;
+ }
- if (snprintf(command_new, BUFFER_SIZE, "%s '%s'", command_name, repo))
- exec_git = true;
+ if (0 > snprintf(buffer, sizeof(buffer), "%s %s", command, quoted_repo)) {
+ fprintf(stderr, "error: failed to generate git-shell command\n");
+ rv = 1;
+ goto cleanup;
+ }
-cleanup:
- free(repo);
- free(command_orig);
- free(command_name);
+ free(command);
+ free(repo);
+ free(quoted_repo);
- if (exec_git) {
- execlp("git-shell", "git-shell", "-c", command_new, NULL);
+ execlp("git-shell", "git-shell", "-c", buffer, NULL);
// execlp only returns on error, then something bad happened
fprintf(stderr, "error: failed to execute git-shell\n");
- rv = 1;
+ return 1; // avoid freeing repo again
}
+cleanup:
+ free(repo);
+ free(quoted_repo);
return rv;
}
diff --git a/src/blogc-git-receiver/shell.h b/src/blogc-git-receiver/shell.h
index c05e4e1..ec77db1 100644
--- a/src/blogc-git-receiver/shell.h
+++ b/src/blogc-git-receiver/shell.h
@@ -9,6 +9,7 @@
#ifndef _SHELL_H
#define _SHELL_H
+char* bgr_shell_get_repo(const char *command);
int bgr_shell(int argc, char *argv[]);
#endif /* _SHELL_H */