diff options
Diffstat (limited to 'src/manifest.c')
-rw-r--r-- | src/manifest.c | 192 |
1 files changed, 170 insertions, 22 deletions
diff --git a/src/manifest.c b/src/manifest.c index f889de2..5ed3bab 100644 --- a/src/manifest.c +++ b/src/manifest.c @@ -3,6 +3,7 @@ */ #include "spm.h" #include <fnmatch.h> +#include "url.h" #define PACKAGE_MIN_DELIM 2 /** @@ -60,8 +61,8 @@ Manifest *manifest_from(const char *package_dir) { char **parts = split(fsdata->files[i], "-"); // Replace invalid character with a hyphen - replace_text(parts[0], "*", "-"); - replace_text(fsdata->files[i], "*", "-"); + replace_text(parts[0], SPM_MANIFEST_NODATA, "-"); + replace_text(fsdata->files[i], SPM_MANIFEST_NODATA, "-"); // Populate `ManifestPackage` record info->packages[i]->size = (size_t) get_file_size(fsdata->files[i]); @@ -100,14 +101,13 @@ void manifest_free(Manifest *info) { * @return */ int manifest_write(Manifest *info) { - const char *filename = "manifest.dat"; + char *reqs = NULL; char path[PATH_MAX]; memset(path, '\0', sizeof(path)); - sprintf(path, "%s%c%s", SPM_GLOBAL.user_config_basedir, DIRSEP, filename); - FILE *fp = fopen(path, "w+"); - char *reqs = NULL; + strcpy(path, SPM_GLOBAL.package_manifest); - // A little too much information (debug?) + FILE *fp = fopen(path, "w+"); +#ifdef _DEBUG if (SPM_GLOBAL.verbose) { for (size_t i = 0; i < info->records; i++) { printf("%-20s: %s\n" @@ -129,56 +129,183 @@ int manifest_write(Manifest *info) { printf("\n"); } } +#endif printf("Generating manifest file: %s\n", path); + fprintf(fp, "%s\n", SPM_MANIFEST_HEADER); + char data[BUFSIZ]; for (size_t i = 0; i < info->records; i++) { // write CSV-like manifest - char data[BUFSIZ]; memset(data, '\0', BUFSIZ); char *dptr = data; float percent = (((float)i + 1) / info->records) * 100; - printf("[%3.0f%%] %s\n", percent, info->packages[i]->archive); + if (SPM_GLOBAL.verbose) { + printf("[%3.0f%%] %s\n", percent, info->packages[i]->archive); + } reqs = join(info->packages[i]->requirements, ","); + char *archive = join((char *[]) {SPM_GLOBAL.package_dir, info->packages[i]->archive, NULL}, DIRSEPS); + char *checksum_sha256 = sha256sum(archive); + sprintf(dptr, "%s|" // archive "%zu|" // size "%s|" // name "%s|" // version "%s|" // revision "%zu|" // requirements_records - "%s" // requirements + "%s|" // requirements + "%s" // checksum_md5 , info->packages[i]->archive, info->packages[i]->size, info->packages[i]->name, info->packages[i]->version, info->packages[i]->revision, info->packages[i]->requirements_records, - reqs ? reqs : "*"); - fprintf(fp, "%s\n", dptr); + reqs ? reqs : SPM_MANIFEST_NODATA, + checksum_sha256 ? checksum_sha256 : SPM_MANIFEST_NODATA); + fprintf(fp, "%s\n", dptr); free(reqs); + if (checksum_sha256 != NULL) + free(checksum_sha256); } fclose(fp); return 0; } /** + * + * @param url + * @param dest + * @return + */ +int fetch(const char *url, const char *dest) { + URL_FILE *handle = NULL; + FILE *outf = NULL; + size_t chunk_size = 0xffff; + size_t nread = 0; + char *buffer = calloc(chunk_size + 1, sizeof(char)); + if (!buffer) { + perror("fetch buffer too big"); + return -1; + } + + handle = url_fopen(url, "r"); + if(!handle) { + printf("couldn't url_fopen() %s\n", url); + return 2; + } + + outf = fopen(dest, "wb+"); + if(!outf) { + perror("couldn't open fread output file\n"); + return 1; + } + + do { + nread = url_fread(buffer, 1, chunk_size, handle); + if (handle->http_status >= 400) { + free(buffer); + fclose(outf); + if (exists(dest) == 0) { + unlink(dest); + } + + long http_status = handle->http_status; + url_fclose(handle); + return http_status; + } + fwrite(buffer, 1, nread, outf); + } while (nread); + + free(buffer); + fclose(outf); + url_fclose(handle); + return 0; +} + +int manifest_validate(void) { + size_t line_count; + int problems; + char data[BUFSIZ]; + FILE *fp; + + if (exists(SPM_GLOBAL.package_manifest) != 0) { + return -1; + } + + if ((fp = fopen(SPM_GLOBAL.package_manifest, "r")) == NULL) { + perror(SPM_GLOBAL.package_manifest); + return -2; + } + + line_count = 0; + problems = 0; + while (fgets(data, BUFSIZ, fp) != NULL) { + int separators; + if (line_count == 0) { + if (strncmp(data, SPM_MANIFEST_HEADER, strlen(SPM_MANIFEST_HEADER)) != 0) { + fprintf(stderr, "Invalid manifest header: %s (expecting '%s')\n", strip(data), SPM_MANIFEST_HEADER); + problems++; + line_count++; + } + } + else if ((separators = num_chars(data, SPM_MANIFEST_SEPARATOR)) != SPM_MANIFEST_SEPARATOR_MAX) { + fprintf(stderr, "Invalid manifest record on line %zu: %s (expecting %d separators, found %d)\n", line_count, strip(data), SPM_MANIFEST_SEPARATOR_MAX, separators); + problems++; + } + line_count++; + } + return problems; +} +/** * Read the package manifest stored in the configuration directory * @return `Manifest` structure */ -Manifest *manifest_read(void) { - const char *filename = "manifest.dat"; +Manifest *manifest_read(char *file_or_url) { + FILE *fp = NULL; + char *filename = SPM_MANIFEST_FILENAME; char path[PATH_MAX]; - memset(path, '\0', sizeof(path)); - sprintf(path, "%s%c%s", SPM_GLOBAL.user_config_basedir, DIRSEP, filename); - FILE *fp = fopen(path, "r+"); - if (!fp) { - perror(filename); - return NULL; + + // When file_or_url is NULL we want to use the global manifest + if (file_or_url == NULL) { + // TODO: move this out + strcpy(path, SPM_GLOBAL.package_manifest); } + else { + strcpy(path, file_or_url); + } + + // Handle receiving a path without the manifest filename + // by appending the manifest to the path + if (endswith(path, filename) != 0) { + strcat(path, DIRSEPS); + strcat(path, filename); + } + + if (exists(path) != 0) { + // TODO: Move this out + char *remote_manifest = join((char *[]) {"http://astroconda.org/spm", SPM_GLOBAL.repo_target, filename, NULL}, DIRSEPS); + int fetch_status = fetch(remote_manifest, path); + if (fetch_status >= 400) { + fprintf(stderr, "HTTP %d: %s: %s\n", fetch_status, http_response_str(fetch_status), remote_manifest); + free(remote_manifest); + return NULL; + } + free(remote_manifest); + } + + int valid = 0; size_t total_records = 0; char data[BUFSIZ]; char *dptr = data; memset(dptr, '\0', BUFSIZ); + fp = fopen(path, "r+"); + if (!fp) { + perror(filename); + fprintf(SYSERROR); + return NULL; + } + while (fgets(dptr, BUFSIZ, fp) != NULL) { total_records++; } @@ -187,14 +314,31 @@ Manifest *manifest_read(void) { Manifest *info = (Manifest *)calloc(1, sizeof(Manifest)); info->packages = (ManifestPackage **)calloc(total_records + 1, sizeof(ManifestPackage *)); + if ((valid = manifest_validate()) != 0) { + return NULL; + } + // Begin parsing the manifest + char separator = SPM_MANIFEST_SEPARATOR; size_t i = 0; + + // Consume header + if (fgets(dptr, BUFSIZ, fp) == NULL) { + // file is probably empty + return NULL; + } + while (fgets(dptr, BUFSIZ, fp) != NULL) { dptr = strip(dptr); char *garbage; - char **parts = split(dptr, "|"); + char **parts = split(dptr, &separator); + char *_origin = dirname(path); info->packages[i] = (ManifestPackage *)calloc(1, sizeof(ManifestPackage)); + + strncpy(info->packages[i]->origin, _origin, strlen(_origin)); + free(_origin); + strncpy(info->packages[i]->archive, parts[0], strlen(parts[0])); info->packages[i]->size = strtoul(parts[1], &garbage, 10); strncpy(info->packages[i]->name, parts[2], strlen(parts[2])); @@ -203,10 +347,14 @@ Manifest *manifest_read(void) { info->packages[i]->requirements_records = (size_t) atoi(parts[5]); info->packages[i]->requirements = NULL; - if (strncmp(parts[6], "*", 2) != 0) { + if (strncmp(parts[6], SPM_MANIFEST_NODATA, strlen(SPM_MANIFEST_NODATA)) != 0) { info->packages[i]->requirements = split(parts[6], ","); } + if (strncmp(parts[7], SPM_MANIFEST_NODATA, strlen(SPM_MANIFEST_NODATA)) != 0) { + strncpy(info->packages[i]->checksum_sha256, parts[7], strlen(parts[7])); + } + split_free(parts); info->records = i; i++; |