marrakesh/mrk-srv.c

454 lines
10 KiB
C

#include <Eina.h>
#include <Ecore.h>
#include <Eet.h>
#include <Ecore_Ipc.h>
#include <Ecore_File.h>
#include "mrk-proto.h"
#include "mrk-db.h"
static const char *sane_name_veto[] = {"../", "./", "/", NULL};
static const char *sane_name_ok = "01234567890-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
static char *repodir = "./repo";
static char *build_sh = "./mrk-srv-build.sh";
static int
sane_forbidden_path(const char *file, const char **forbidden)
{
int i;
for (i = 0; forbidden[i]; i++)
{
if (strstr(forbidden[i], file)) return 0;
}
return 1;
}
static int
sane_allowed_path(const char *file, const char *allowed)
{
int i, j;
for (i = 0; file[i]; i++)
{
int ok;
ok = 0;
for (j = 0; allowed[j]; j++)
{
if (file[i] == allowed[j])
{
ok = 1;
break;
}
}
if (!ok) return 0;
}
return 1;
}
typedef struct
{
Ecore_Ipc_Client *client;
char *file;
char *dir;
FILE *f;
Ecore_Exe *exe;
char *arch;
char *version;
} Client;
static Ecore_Ipc_Server *ipc = NULL;
static Eina_List *clients = NULL;
static Client *
client_find(Ecore_Ipc_Client *ipc_client)
{
Eina_List *l;
Client *c;
EINA_LIST_FOREACH(clients, l, c)
{
if (c->client == ipc_client) return c;
}
return NULL;
}
static Client *
client_exe_find(Ecore_Exe *exe)
{
Eina_List *l;
Client *c;
EINA_LIST_FOREACH(clients, l, c)
{
if (c->exe == exe) return c;
}
return NULL;
}
static Client *
client_new(void)
{
Client *c;
c = calloc(1, sizeof(Client));
clients = eina_list_append(clients, c);
return c;
}
static void
client_free(Client *c)
{
if (c->f) fclose(c->f);
if (c->file) free(c->file);
if (c->dir)
{
ecore_file_recursive_rm(c->dir);
free(c->dir);
}
clients = eina_list_remove(clients, c);
free(c);
}
static void
client_upload_new(Client *c)
{
char tmp[4096];
Eina_Tmpstr *s = NULL;
if (c->f) fclose(c->f);
if (c->dir)
{
ecore_file_recursive_rm(c->dir);
free(c->dir);
c->dir = NULL;
}
if (eina_file_mkdtemp("marrekesh-up-XXXXXX", &s))
{
c->dir = strdup(s);
eina_tmpstr_del(s);
if (c->dir)
{
snprintf(tmp, sizeof(tmp), "%s/%s", c->dir, c->file);
c->f = fopen(tmp, "wb");
}
}
}
static void
client_upload_end(Client *c)
{
char tmp[4096];
if (c->f)
{
fclose(c->f);
c->f = NULL;
snprintf(tmp, sizeof(tmp), "%s %s %s", build_sh, c->dir, repodir);
c->exe = ecore_exe_pipe_run(tmp,
ECORE_EXE_TERM_WITH_PARENT |
ECORE_EXE_NOT_LEADER, c);
}
}
static void
client_upload_fail(Client *c)
{
if (c->f) fclose(c->f);
if (c->file) free(c->file);
if (c->dir)
{
ecore_file_recursive_rm(c->dir);
free(c->dir);
}
c->f = NULL;
c->file = NULL;
c->dir = NULL;
}
static void
client_upload_data(Client *c, void *data, int size)
{
if (c->f) fwrite(data, size, 1, c->f);
}
static void
client_send(Client *c, const char *name)
{
char tmp[4096];
char *lnk;
if (!c->arch) return;
snprintf(tmp, sizeof(tmp), "%s/%s/%s", repodir, c->arch, name);
lnk = ecore_file_readlink(tmp);
if (!lnk)
ecore_ipc_client_send(c->client, 10, M_DOWN_START, 0, 0, 0, NULL, 0);
else
{
FILE *f;
f = fopen(tmp, "rb");
if (!f)
ecore_ipc_client_send(c->client, 10, M_DOWN_START, 0, 0, 0, NULL, 0);
else
{
ecore_ipc_client_send(c->client, 10, M_DOWN_START, 0, 0, 0, lnk, strlen(lnk));
for (;;)
{
size_t size;
char buf[10000];
size = fread(buf, 1, 10000, f);
if (size <= 0) break;
ecore_ipc_client_send(c->client, 10, M_DOWN_DATA, 0, 0, 0, buf, size);
}
ecore_ipc_client_send(c->client, 10, M_DOWN_END, 0, 0, 0, NULL, 0);
fclose(f);
}
}
}
static void
client_src_send(Client *c, const char *name)
{
char tmp[4096];
char *lnk;
snprintf(tmp, sizeof(tmp), "%s/src/%s", repodir, name);
lnk = ecore_file_readlink(tmp);
if (!lnk)
ecore_ipc_client_send(c->client, 10, M_SRC_START, 0, 0, 0, NULL, 0);
else
{
FILE *f;
f = fopen(tmp, "rb");
if (!f)
ecore_ipc_client_send(c->client, 10, M_SRC_START, 0, 0, 0, NULL, 0);
else
{
ecore_ipc_client_send(c->client, 10, M_SRC_START, 0, 0, 0, lnk, strlen(lnk));
for (;;)
{
size_t size;
char buf[10000];
size = fread(buf, 1, 10000, f);
if (size <= 0) break;
ecore_ipc_client_send(c->client, 10, M_SRC_DATA, 0, 0, 0, buf, size);
}
ecore_ipc_client_send(c->client, 10, M_SRC_END, 0, 0, 0, NULL, 0);
fclose(f);
}
}
}
static int
arch_ok(const char *arch)
{
if (!strcmp(arch, "x86_64-linux")) return 1;
return 0;
}
static int
version_ok(const char *arch)
{
return 1;
}
static Eina_Bool
_ipc_cb_add(void *data, int type, void *event)
{
Ecore_Ipc_Event_Client_Add *e = event;
Client *c = client_new();
c->client = e->client;
return EINA_TRUE;
}
static Eina_Bool
_ipc_cb_del(void *data, int type, void *event)
{
Ecore_Ipc_Event_Client_Del *e = event;
Client *c = client_find(e->client);
if (c) client_free(c);
return EINA_TRUE;
}
static Eina_Bool
_ipc_cb_dat(void *data, int type, void *event)
{
Ecore_Ipc_Event_Client_Data *e = event;
Client *c = client_find(e->client);
// e->major e->minor e->ref e->ref_to e->response | e->data e->size
if (e->major != 10) return EINA_TRUE;
switch (e->minor)
{
case M_UP_START:
// e->data == filename
if ((e->size > 0) && (e->size < 1000))
{
if (c->file) free(c->file);
c->file = malloc(e->size + 1);
memcpy(c->file, e->data, e->size);
c->file[e->size] = 0;
if (sane_forbidden_path(c->file, sane_name_veto) &&
sane_allowed_path(c->file, sane_name_ok))
{
client_upload_new(c);
}
else client_upload_fail(c);
}
else client_upload_fail(c);
break;
case M_UP_DATA:
// e->data == chunk of bytes to append
if ((e->size > 0) && (e->size <= 10000))
{
client_upload_data(c, e->data, e->size);
}
else client_upload_fail(c);
break;
case M_UP_END:
// e->data == ignored (EOF)
client_upload_end(c);
break;
case M_QRY_LIST:
// e->data == category (none if all)
break;
case M_QRY_SEARCH:
// e->data == query string
break;
case M_QRY_GET:
if ((e->size > 0) && (e->size < 1000))
{
char *name = malloc(e->size + 1);
if (name)
{
memcpy(name, e->data, e->size);
name[e->size] = 0;
client_send(c, name);
free(name);
}
}
// e->data == appname (Terminology, Rage etc.)
break;
case M_QRY_GETSRC:
if ((e->size > 0) && (e->size < 1000))
{
char *name = malloc(e->size + 1);
if (name)
{
memcpy(name, e->data, e->size);
name[e->size] = 0;
client_src_send(c, name);
free(name);
}
}
// e->data == appname (Terminology, Rage etc.)
break;
case M_ID_UUID:
break;
case M_ID_VERSION:
if ((e->size > 0) && (e->size < 1000))
{
char *version = malloc(e->size + 1);
if (version)
{
memcpy(version, e->data, e->size);
version[e->size] = 0;
if (version_ok(version))
{
if (c->version) free(c->version);
c->version = version;
}
else free(version);
}
}
break;
case M_ID_ARCH:
if ((e->size > 0) && (e->size < 1000))
{
char *arch = malloc(e->size + 1);
if (arch)
{
memcpy(arch, e->data, e->size);
arch[e->size] = 0;
if (arch_ok(arch))
{
if (c->arch) free(c->arch);
c->arch = arch;
}
else free(arch);
}
}
break;
default:
break;
}
return EINA_TRUE;
}
static Eina_Bool
_exe_cb_del(void *data, int type, void *event)
{
Ecore_Exe_Event_Del *ev = event;
Client *c = client_exe_find(ev->exe);
if (c)
{
if (ev->exit_code == 0)
ecore_ipc_client_send(c->client, 10, M_UP_OK, 0, 0, 0, NULL, 0);
else
ecore_ipc_client_send(c->client, 10, M_UP_FAIL, 0, 0, 0, NULL, 0);
c->exe = NULL;
}
return EINA_TRUE;
}
static int
ipc_init(void)
{
ipc = ecore_ipc_server_add(ECORE_IPC_REMOTE_SYSTEM, "0.0.0.0", 10077, NULL);
if (!ipc) return 0;
ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_ADD, _ipc_cb_add, NULL);
ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DEL, _ipc_cb_del, NULL);
ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DATA, _ipc_cb_dat, NULL);
return 1;
}
int
main(int argc, char **argv)
{
if (argc < 2)
{
printf("usage:\n"
" mrk-srv [REPO_DIR] [BUILD_SCRIPT]\n"
"\n");
exit(1);
}
eina_init();
ecore_init();
eet_init();
ecore_file_init();
ecore_ipc_init();
if (argc > 1) repodir = ecore_file_realpath(argv[1]);
if (argc > 2) build_sh = ecore_file_realpath(argv[2]);
if (ipc_init())
{
ecore_exe_run_priority_set(10);
ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _exe_cb_del, NULL);
ecore_main_loop_begin();
}
ecore_ipc_shutdown();
ecore_file_shutdown();
eet_shutdown();
ecore_shutdown();
eina_shutdown();
return 0;
}