#include #include #include #include #include #include #include #include #include #include #include #include "memory.h" #include "dupstr.h" #include "subproc.h" #include "uchars.h" /* * The same routine MsiCloseHandle is used by our client to dispose of * three kinds of structure that _we_ think of as completely different * types. So we must ensure all three start with distinct magic number * fields, so as to know what kind we're being asked to close. */ typedef struct MsiTypePrefix { enum { MAIN = 0x2B7FB8B8, VIEW = 0x1570B0E3, RECORD = 0x62365065 } type; } MsiTypePrefix; typedef struct MsiMainCtx { MsiTypePrefix t; char *tempdir; char *outfile; char **args; int nargs, argsize; } MsiMainCtx; /* * The Msi functions will expect to identify things by 32-bit handles, * not machine-word sized pointers. So we must keep a list of handles * we've allocated. */ typedef uint32_t MsiHandle; static void **msi_handles = NULL; static size_t n_msi_handles = 0, msi_handles_size = 0; static MsiHandle make_handle(void *ptr) { size_t index; if (n_msi_handles >= msi_handles_size) { msi_handles_size = n_msi_handles * 5 / 4 + 512; msi_handles = sresize(msi_handles, msi_handles_size, void *); } index = n_msi_handles++; msi_handles[index] = ptr; /* * A mild error-correcting code, to ensure our handles make sense. */ return index * 59 + 17; } static void *lookup_handle(MsiHandle h) { size_t index; assert(h % 59 == 17); index = h / 59; assert(index < n_msi_handles); return msi_handles[index]; } static MsiMainCtx *lookup_handle_main(MsiHandle handle) { MsiMainCtx *toret = lookup_handle(handle); assert(toret->t.type == MAIN); return toret; } uint32_t MsiOpenDatabaseW(const char16_t *filename, const char16_t *persist, MsiHandle *out_handle) { MsiMainCtx *ctx = snew(MsiMainCtx); ctx->t.type = MAIN; ctx->outfile = ascii(filename, true); close(open(ctx->outfile, O_CREAT | O_WRONLY, 0666)); ctx->outfile = realpath(ctx->outfile, NULL); unlink(ctx->outfile); ctx->tempdir = dupcat(ctx->outfile, "-msiXXXXXX", cNULL); if (!mkdtemp(ctx->tempdir)) err(1, "%s: mkdtemp", ctx->tempdir); ctx->nargs = 0; ctx->argsize = 16; ctx->args = snewn(ctx->argsize, char *); ctx->args[ctx->nargs++] = dupstr("sh"); ctx->args[ctx->nargs++] = dupstr("-c"); ctx->args[ctx->nargs++] = dupstr("cd \"$0\" && \"$@\""); ctx->args[ctx->nargs++] = dupstr(ctx->tempdir); ctx->args[ctx->nargs++] = dupstr("msibuild"); ctx->args[ctx->nargs++] = dupstr(ctx->outfile); *out_handle = make_handle(ctx); return 0; } uint32_t MsiDatabaseImportW(MsiHandle handle, const char16_t *folder, const char16_t *file) { MsiMainCtx *ctx = lookup_handle_main(handle); system_argv("sh", "-c", "cd \"$0\" && cp \"$1\" \"$2\"", ascii(folder, true), ascii(file, true), ctx->tempdir, cNULL); if (ctx->nargs + 2 >= ctx->argsize) { ctx->argsize = ctx->nargs * 5 / 4 + 16; ctx->args = sresize(ctx->args, ctx->argsize, char *); } ctx->args[ctx->nargs++] = dupstr("-i"); ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, "/", ascii(file, true), cNULL); return 0; } typedef struct MsiView { MsiTypePrefix t; FILE *fp; char *targetdir; MsiMainCtx *ctx; } MsiView; static MsiView *lookup_handle_view(MsiHandle handle) { MsiView *toret = lookup_handle(handle); assert(toret->t.type == VIEW); return toret; } uint32_t MsiDatabaseOpenViewW(MsiHandle handle, const char16_t *query, MsiHandle *outhandle) { MsiMainCtx *ctx = lookup_handle_main(handle); MsiView *view = snew(MsiView); view->t.type = VIEW; view->ctx = ctx; char *cquery = ascii(query, false); if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `_Streams`")) view->fp = NULL; /* special case */ else { if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Binary`")) { view->fp = fopen(dupcat(ctx->tempdir, "/", "Binary.idt", cNULL), "a"); view->targetdir = dupcat(ctx->tempdir, "/", "Binary", cNULL); } else if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Icon`")) { view->fp = fopen(dupcat(ctx->tempdir, "/", "Icon.idt", cNULL), "a"); view->targetdir = dupcat(ctx->tempdir, "/", "Icon", cNULL); } else errx(1, "unrecognised query: %s", cquery); if (!view->fp) err(1, "open"); if (mkdir(view->targetdir, 0777) < 0) err(1, "%s: mkdir", view->targetdir); } *outhandle = make_handle(view); return 0; } uint32_t MsiViewExecute(MsiHandle handle, void *params) { lookup_handle_view(handle); return 0; } typedef struct MsiRecord { MsiTypePrefix t; char *name, *data; } MsiRecord; static MsiRecord *lookup_handle_record(MsiHandle handle) { MsiRecord *toret = lookup_handle(handle); assert(toret->t.type == RECORD); return toret; } MsiHandle MsiCreateRecord(uint32_t nparams) { MsiRecord *rec = snew(MsiRecord); rec->t.type = RECORD; if (nparams != 2) errx(1, "bad MsiCreateRecord param count %u", (unsigned)nparams); rec->name = rec->data = NULL; return make_handle(rec); } uint32_t MsiRecordSetStringW(MsiHandle handle, uint32_t field, char16_t *value) { MsiRecord *rec = lookup_handle_record(handle); if (field != 1) errx(1, "bad MsiRecordSetString param index %u", (unsigned)field); rec->name = ascii(value, false); return 0; } uint32_t MsiRecordSetStreamW(MsiHandle handle, uint32_t field, char16_t *path) { MsiRecord *rec = lookup_handle_record(handle); if (field != 2) errx(1, "bad MsiRecordSetStream param index %u", (unsigned)field); rec->data = ascii(path, true); return 0; } uint32_t MsiViewModify(MsiHandle viewhandle, uint32_t mode, MsiHandle rechandle) { MsiView *view = lookup_handle_view(viewhandle); MsiRecord *rec = lookup_handle_record(rechandle); if (view->fp) { system_argv("sh", "-c", "cp \"$0\" \"$1\"/\"$2\"", rec->data, view->targetdir, rec->name, cNULL); fprintf(view->fp, "%s\t%s\r\n", rec->name, rec->name); } else { MsiMainCtx *ctx = view->ctx; if (ctx->nargs + 3 >= ctx->argsize) { ctx->argsize = ctx->nargs * 5 / 4 + 16; ctx->args = sresize(ctx->args, ctx->argsize, char *); } ctx->args[ctx->nargs++] = dupstr("-a"); ctx->args[ctx->nargs++] = dupstr(rec->name); ctx->args[ctx->nargs++] = dupstr(rec->data); } return 0; } uint32_t MsiCloseHandle(MsiHandle handle) { MsiTypePrefix *t = lookup_handle(handle); if (t->type == VIEW) { MsiView *view = (MsiView *)t; if (view->fp) fclose(view->fp); } return 0; } uint32_t MsiDatabaseCommit(MsiHandle handle) { MsiMainCtx *ctx = lookup_handle_main(handle); printf("commit:"); for (int i = 0; i < ctx->nargs; i++) { printf(" '"); for (const char *p = ctx->args[i]; *p; p++) { if (*p == '\'') printf("'\\''"); else putchar(*p); } printf("'"); } printf("\n"); if (ctx->nargs + 1 >= ctx->argsize) { ctx->argsize = ctx->nargs * 5 / 4 + 16; ctx->args = sresize(ctx->args, ctx->argsize, char *); } ctx->args[ctx->nargs++] = NULL; system_argv_array(ctx->args); return 0; }