コード例 #1
0
ファイル: update-main.c プロジェクト: jpaxy/cve-check-tool
/**
 * Main entry.
 */
int main(int argc, char **argv)
{
        autofree(GError) *error = NULL;
        autofree(GOptionContext) *context = NULL;
        int ret = EXIT_FAILURE;

        LIBXML_TEST_VERSION
        context = g_option_context_new(" - cve update");
        g_option_context_add_main_entries(context, _entries, NULL);
        if (!g_option_context_parse(context, &argc, &argv, &error)) {
                g_printerr("Invalid options: %s\n", error->message);
                goto end;
        }

        if (_show_version) {
                show_version();
                ret = EXIT_SUCCESS;
                goto end;
        }

        if (update_db(_quiet)) {
                ret = EXIT_SUCCESS;
        } else {
                fprintf(stderr, "Failed to update database\n");
        }

end:
        xmlCleanupParser();
        return ret;
}
コード例 #2
0
ファイル: rpm.c プロジェクト: fenrus75/cve-check-tool
bool rpm_is_patched(struct source_package_t *pkg, char *id)
{
        bool ret = false;
        /* Determine if its patched. */
        autofree(gchar) *pnamet = g_ascii_strdown((gchar*)id, -1);
        autofree(gchar) *pname = g_strdup_printf("%s.patch", pnamet);
        autofree(gchar) *tpath = g_build_filename(G_DIR_SEPARATOR_S, pkg->path, pname, NULL);

        ret = g_file_test(tpath, G_FILE_TEST_EXISTS);
        return ret;
}
コード例 #3
0
ファイル: rpm.c プロジェクト: fenrus75/cve-check-tool
struct source_package_t *rpm_inspect_srpm(const char *dir, const char *name,
                                          const char *version, const char *release)
{
        struct source_package_t *t = NULL;
        autofree(gchar) *path = NULL;
        autofree(gchar) *cmdline = NULL;
        autofree(GError) *error = NULL;
        gchar **splits = NULL;
        int exit = 0;
        autofree(gchar) *output = NULL;

        t = calloc(1, sizeof(struct source_package_t));
        if (!t) {
                return NULL;
        }

        path = g_strdup_printf("%s%s%s-%s-%s.src.rpm", dir, G_DIR_SEPARATOR_S,
                name, version, release);
        if (!path) {
                free(t);
                return NULL;
        }
        if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
                printf("Required source rpm not present: %s\n", path);
                free(t);
                return NULL;
        }
        cmdline = g_strdup_printf("rpm -qp --queryformat \'[%%{PATCH}\t]\n\' %s", path);
        if (!cmdline) {
                free(t);
                return NULL;
        }
        if (!g_spawn_command_line_sync(cmdline, &output, NULL, &exit, &error)) {
                g_printerr("Unable to run command: %s\n", error->message);
                free(t);
                return NULL;
        }
        if (exit != 0) {
                printf("Abnormal exit code for package %s: %s\n", name, output);
        }
        splits = g_strsplit(output, "\t", -1);
        if (g_strv_length(splits) > 0) {
                t->extra = splits;
        } else {
                g_strfreev(splits);
        }

        t->name = g_strdup(name);
        t->version = g_strdup(version);
        return t;
}
コード例 #4
0
ファイル: check-inifile.c プロジェクト: bryteise/libnica
END_TEST

START_TEST(nc_inifile_bad_test)
{
        const char *t_path = TOP_DIR "/tests/ini/sectionless.ini";
        autofree(NcHashmap) *f = NULL;

        f = nc_ini_file_parse(t_path);
        fail_if(f != NULL, "Parsed illegal sectionless INI file");

        t_path = TOP_DIR "/tests/ini/empty_key.ini";
        f = nc_ini_file_parse(t_path);
        fail_if(f != NULL, "Parsed illegal INI file with empty keys");

        t_path = TOP_DIR "/tests/ini/just_assign.ini";
        f = nc_ini_file_parse(t_path);
        fail_if(f != NULL, "Parsed illegal assign-only INI file");

        t_path = TOP_DIR "/tests/ini/broken_section_start.ini";
        f = nc_ini_file_parse(t_path);
        fail_if(f != NULL, "Parsed illegal broken-section-start INI File");

        t_path = TOP_DIR "/tests/ini/broken_section_end.ini";
        f = nc_ini_file_parse(t_path);
        fail_if(f != NULL, "Parsed illegal broken-section-end INI File");
}
コード例 #5
0
ファイル: html.c プロジェクト: fenrus75/cve-check-tool
static inline bool load_template(const char *tmpl_name, gchar **ret)
{
        autofree(GError) *error = NULL;

        if (!g_file_get_contents(tmpl_name, ret, NULL, &error)) {
                g_printerr("Unable to access mandatory template: %s\n", tmpl_name);
                *ret = NULL;
                return false;
        }
        return true;
}
コード例 #6
0
ファイル: rpm.c プロジェクト: fenrus75/cve-check-tool
bool srpm_is_patched(struct source_package_t *t, char *id)
{
        if (!t->extra) {
                return false;
        }

        autofree(gchar) *pnamet = g_ascii_strdown((gchar*)id, -1);
        autofree(gchar) *pname = g_strdup_printf("%s.patch", pnamet);
        gchar **list = t->extra;

        for (uint i = 0; i < g_strv_length(list); i++) {
                list[i] = g_strchomp(list[i]);
                if (g_str_equal(list[i], "")) {
                        continue;
                }
                if (g_str_equal(pname, list[i])) {
                        return true;
                }
        }

        return false;
}
コード例 #7
0
ファイル: fetch.c プロジェクト: miguelinux/cve-check-tool
bool gunzip_file(const char *path)
{
        GFile *in = NULL, *out = NULL;
        autofree(gchar) *newpath = NULL;
        autofree(GFileInputStream) *fis = NULL;
        autofree(GFileOutputStream) *fos = NULL;
        autofree(GOutputStream) *cos = NULL;
        autofree(GZlibDecompressor) *conv = NULL;
        gsize ret;

        newpath = g_strdup(path);

        if (g_str_has_suffix(newpath, ".gz")) {
                newpath = str_replace(newpath, ".gz", "");
        }

        in = g_file_new_for_path(path);
        out = g_file_new_for_path(newpath);

        fis = g_file_read(in, NULL, NULL);
        if (!fis) {
                return NULL;
        }
        fos = g_file_replace(out, NULL, FALSE, 0, NULL, NULL);
        if (!fos) {
                return NULL;
        }

        conv = g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP);
        cos = g_converter_output_stream_new(G_OUTPUT_STREAM(fos), G_CONVERTER(conv));
        if (!cos) {
                return NULL;
        }
        ret = g_output_stream_splice(cos, G_INPUT_STREAM(fis), G_OUTPUT_STREAM_SPLICE_NONE, NULL, NULL);
        return (ret > 0 ? true : false );
}
コード例 #8
0
ファイル: check-inifile.c プロジェクト: bryteise/libnica
END_TEST

START_TEST(nc_inifile_good_test)
{
        const char *wellformed[] = { TOP_DIR "/tests/ini/wellformed.ini",
                                     TOP_DIR "/tests/ini/valid_padding.ini" };

        for (size_t i = 0; i < sizeof(wellformed) / sizeof(wellformed[0]);
             i++) {
                autofree(NcHashmap) *f = NULL;
                char *ret = NULL;

                f = nc_ini_file_parse(wellformed[i]);
                fail_if(f == NULL, "Failed to parse wellformed.ini");

                fail_if(!nc_hashmap_contains(f, "John"),
                        "INI File missing \"John\" section");

                ret = nc_hashmap_get(nc_hashmap_get(f, "John"), "alive");
                fail_if(!ret, "Failed to get known value from INI file");
                fail_if(!streq(ret, "true"), "Incorrect value in INI file");

                fail_if(!nc_hashmap_contains(f, "Alice"),
                        "INI File missing \"Alice\" section");
                ret = nc_hashmap_get(nc_hashmap_get(f, "Alice"), "alive");
                fail_if(!ret, "Failed to get known value from INI file");
                fail_if(!streq(ret, "false"),
                        "Incorrect value in INI file #2");

                ret = nc_hashmap_get(nc_hashmap_get(f, "John"), "Random");
                fail_if(ret, "Got unexpected key in section");

                ret = nc_hashmap_get(nc_hashmap_get(f, "Bob"), "Random");
                fail_if(ret, "Got unexpected section");
        }
}
コード例 #9
0
ファイル: html.c プロジェクト: fenrus75/cve-check-tool
bool write_report(CveCheckTool *self,  char *filename)
{
        autofree(gchar) *bottom, *package, *top;
        autofree(GError) *error = NULL;
        bottom = package = top = NULL;
        autofree(GHashTable) *macros = NULL;
        autofree(gchar) *aff = NULL;
        autofree(gchar) *output = NULL;
        autofree(gchar) *body = NULL;
        GHashTableIter iter;
        gchar *key = NULL;
        struct source_package_t *v = NULL;
        GList *c = NULL;
        struct cve_entry_t *c_entry = NULL;
        gint affected = 0;
        guint row = 0;

        /* Mandatory template files */
        LOAD_TEMPLATE(TMPL_BOTTOM, &bottom);
        LOAD_TEMPLATE(TMPL_PACKAGE, &package);
        LOAD_TEMPLATE(TMPL_TOP, &top);

        macros = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
        g_hash_table_insert(macros, "LNVD_CVE_URI", NVD_CVE_URI);

        g_hash_table_iter_init(&iter, self->db);
        while (g_hash_table_iter_next(&iter, (void**)&key, (void**)&v)) {
                bool hit = false;
                if (!v->issues && !v->patched && !self->show_unaffected) {
                        continue;
                }
                if (!v->issues && self->hide_patched) {
                        continue;
                }

                guint cve_len = 0;

                if (v->issues) {
                        cve_len += g_list_length(v->issues);
                }
                if (v->patched && !self->hide_patched) {
                        cve_len += g_list_length(v->patched);
                }

                g_hash_table_insert(macros, "PACKAGE_NAME", key);

                /* TODO: Collapse redundancy into helper. */
                g_hash_table_insert(macros, "STATUS_CLASS", "not-patched");
                g_hash_table_insert(macros, "STATUS_STRING", "Check");
                for (c = v->issues; c; c = c->next) {
                        autofree(gchar) *ent = NULL;
                        c_entry = g_hash_table_lookup(self->cdb, (gchar*)c->data);

                        if (self->modified > 0 && c_entry->modified > self->modified) {
                                continue;
                        }
                        hit = true;

                        g_hash_table_insert(macros, "LCVEID", c_entry->id);
                        g_hash_table_insert(macros, "CVEID", c_entry->id);
                        g_hash_table_insert(macros, "DESC", c_entry->summary);
                        g_hash_table_insert(macros, "ROW_CLASS", row % 2 ? "even" : "odd");
                        g_hash_table_insert(macros, "SCORE", c_entry->score);

                        /* TODO: Add links .. */
                        ent = g_strdup(package);
                        ent = dotemplate(macros, ent);
                        if (body) {
                                body = _concat(body, ent);
                        } else {
                                body = g_strdup(ent);
                        }
                        ++row;
                }
                g_hash_table_insert(macros, "STATUS_CLASS", "patched");
                g_hash_table_insert(macros, "STATUS_STRING", "Patched");
                if (!self->hide_patched && v->patched) {
                        for (c = v->patched; c; c = c->next) {
                                autofree(gchar) *ent = NULL;
                                c_entry = g_hash_table_lookup(self->cdb, (gchar*)c->data);

                                if (self->modified > 0 && c_entry->modified > self->modified) {
                                        continue;
                                }
                                hit = true;

                                g_hash_table_insert(macros, "LCVEID", c_entry->id);
                                g_hash_table_insert(macros, "CVEID", c_entry->id);
                                g_hash_table_insert(macros, "DESC", c_entry->summary);
                                g_hash_table_insert(macros, "ROW_CLASS", row % 2 ? "even" : "odd");
                                g_hash_table_insert(macros, "SCORE", c_entry->score);

                                /* TODO: Add links .. */
                                ent = g_strdup(package);
                                ent = dotemplate(macros, ent);
                                if (body) {
                                        body = _concat(body, ent);
                                } else {
                                        body = g_strdup(ent);
                                }
                                ++row;
                        }
                }
                if (hit) {
                        ++affected;
                }
        }

        aff = g_strdup_printf("CVE Report for %d package%s", affected,
                affected > 1 ? "s" : "");

        g_hash_table_insert(macros, "AFFECTED_STRING", aff);
        top = dotemplate(macros, top);
        output = g_strdup(top);
        output = _concat(output, body);
        output = _concat(output, bottom);
        /* Write file */
        if (!g_file_set_contents(filename, output, -1, &error)) {
                g_printerr("Unable to write report: %s\n", error->message);
                return false;
        }

        return true;
}
コード例 #10
0
static char *cnvformat( void )
{
	//		フォーマット付き文字列を作成する
	//
#if ( WIN32 || _WIN32 ) && ! __CYGWIN__
#define SNPRINTF _snprintf
#else
#define SNPRINTF snprintf
#endif

	char fstr[1024];
	char *fp;
	int capacity;
	int len;
	char *p;
	
	strncpy( fstr, code_gets(), sizeof fstr );
	fstr[sizeof(fstr)-1] = '\0';
	fp = fstr;
	capacity = 1024;
	p = sbAlloc(capacity);
	len = 0;
	
	CAutoSbFree autofree(&p);
	
	while (1) {
		char fmt[32];
		int i;
		int val_type;
		void *val_ptr;

		// '%' までをコピー
		i = 0;
		while( fp[i] != '\0' && fp[i] != '%' ) {
			i ++;
		}
		cnvformat_expand( &p, &capacity, len, i );
		memcpy( p + len, fp, i );
		len += i;
		fp += i;
		if ( *fp == '\0' ) break;

		// 変換指定を読み fmt にコピー
		i = (int)strspn( fp + 1, " #+-.0123456789" ) + 1;
		strncpy( fmt, fp, sizeof fmt );
		fmt[sizeof(fmt)-1] = '\0';
		if ( i + 1 < (int)(sizeof fmt) ) fmt[i+1] = '\0';
		fp += i;

		char specifier = *fp;
		fp ++;

#if ( WIN32 || _WIN32 ) && ! __CYGWIN__
		if ( specifier == 'I' ) {				// I64 prefix対応(VC++のみ)
			if ((fp[0]=='6')&&(fp[1]='4')) {
				memcpy( fmt+i+1, fp, 3 );
				fmt[i+4] = 0;
				specifier = 'f';
				fp += 3;
			}
		}
#endif

		if ( specifier == '\0' ) break;
		if ( specifier == '%' ) {
			cnvformat_expand( &p, &capacity, len, 1 );
			p[len++] = '%';
			continue;
		}

		// 引数を取得
		if ( code_get() <= PARAM_END ) throw HSPERR_INVALID_FUNCPARAM;
		switch (specifier) {
		case 'd': case 'i': case 'c': case 'o': case 'x': case 'X': case 'u': case 'p':
			val_type = HSPVAR_FLAG_INT;
			val_ptr = HspVarCoreCnvPtr( mpval, HSPVAR_FLAG_INT );
			break;
		case 'f': case 'e': case 'E': case 'g': case 'G':
			val_type = HSPVAR_FLAG_DOUBLE;
			val_ptr = HspVarCoreCnvPtr( mpval, HSPVAR_FLAG_DOUBLE );
			break;
		case 's':
			val_type = HSPVAR_FLAG_STR;
			val_ptr = HspVarCoreCnvPtr( mpval, HSPVAR_FLAG_STR );
			break;
		default:
			throw HSPERR_INVALID_FUNCPARAM;
		}

		// snprintf が成功するまでバッファを広げていき、変換を行う
		while (1) {
			int n;
			int space = capacity - len - 1;
			if ( val_type == HSPVAR_FLAG_INT ) {
				n = SNPRINTF( p + len, space, fmt, *(int *)val_ptr );
			} else if ( val_type == HSPVAR_FLAG_DOUBLE ) {
				n = SNPRINTF( p + len, space, fmt, *(HSPREAL *)val_ptr );
			} else {
				n = SNPRINTF( p + len, space, fmt, (char *)val_ptr );
			}

			if ( n >= 0 && n < space ) {
				len += n;
				break;
			}
			if ( n >= 0 ) {
				space = n + 1;
			} else {
				space *= 2;
				if ( space < 32 ) space = 32;
			}
			cnvformat_expand( &p, &capacity, len, space );
		}
	}
	p[len] = '\0';
	
	char *result = code_stmp(len + 1);
	strcpy(result, p);
	return result;
}
コード例 #11
0
/**
 * A simple JIRA plugin client for development purposes only.
 */
int main(int argc, char **argv)
 {
    int opt,test_case = -1;
    bool verbose_flag = false;
    gchar *jira_cfg_path = "jira.cfg";
    gchar *json_path = NULL;
    autofree(gchar) *jira_issues_json = NULL;
    autofree(gchar) *jira_json = NULL;
    autofree(gchar) *summary = NULL;
    autofree(gchar) *description = g_strdup("This is just a test. Ignore");
    GHashTable *jira_issues = NULL;
    GSList *jira_issues_list = NULL;

    time_t now = time(NULL);

    while ((opt = getopt(argc,argv,"c:ht:V")) != -1)
        switch(opt) {
            case 'c':
                jira_cfg_path = argv[optind-1];
                break;
            case 't':
                test_case = atoi(argv[optind-1]);
                break;
            case 'V':
                verbose_flag = true;
                break;
            case 'h':
                show_usage(argv[0]);
                return 0;
        }
    if (test_case < 0) {
        show_usage(argv[0]);
        return -1;
    }
    printf("\n[Execute: test case %i using %s]\n\n", test_case, jira_cfg_path);
    if (!init_jira_plugin(NULL, jira_cfg_path)) return 1;
    set_verbose(verbose_flag);
    switch(test_case) {
        /* Check if JIRA server is responsive */
        case 1:
            if (!is_jira_alive())
                goto cleanup;
            break;
        /* Do a simple query */
        case 2:
            if (!is_jira_alive())
                goto cleanup;
            if (!build_search_jira_issues(&jira_json))
                 goto cleanup;
            if (!search_jira_issues(jira_json, &jira_issues_json))
                goto cleanup;
            if (!parse_jira_issues(jira_issues_json, &jira_issues_list))
                goto cleanup;
            show_jira_issues_list(jira_issues_list);
            if (!save_jira_issues_csv(
                jira_issues_list,"jira_test2_results.csv"))
                goto cleanup;
            if (!save_jira_issues_xml(jira_issues_list,
                "jira_test2_results.xml"))
                goto cleanup;
            if (!save(jira_json, "jira_test2_json.txt"))
                goto cleanup;
            if (!save(jira_issues_json, "jira_test2_issues_json.txt"))
                goto cleanup;
            free_jira_issues_list(&jira_issues_list);
            break;
        /* Add a new JIRA issue */
        case 3:
            if (!is_jira_alive())
                goto cleanup;
            summary =
            g_strdup_printf("CVE-%s (Test:3)", g_strstrip(ctime(&now)));
            if (!build_new_jira_issue(summary, description, false, &jira_json))
                goto cleanup;
            printf("Adding new JIRA issue: [%s]\n", summary);
            if (!add_new_jira_issue(jira_json))
                goto cleanup;
            g_free(jira_json);
            if (!build_search_jira_issues(&jira_json))
                 goto cleanup;
            if (!search_jira_issues(jira_json, &jira_issues_json))
                goto cleanup;
            if (!parse_jira_issues(jira_issues_json, &jira_issues_list))
                goto cleanup;
            show_jira_issues_list(jira_issues_list);
            if (!save_jira_issues_csv(
                jira_issues_list,"jira_test3_results_add.csv"))
                goto cleanup;
            if (!save_jira_issues_xml(
                jira_issues_list,"jira_test3_results_add.xml"))
                goto cleanup;
            free_jira_issues_list(&jira_issues_list);
            break;
        /* Add a new issue using a self-generated template */
        case 4:
            summary =
            g_strdup_printf("CVE-%s (Test:4)", g_strstrip(ctime(&now)));
            if (!build_new_jira_issue(NULL, NULL, true, &jira_json))
                goto cleanup;
            json_path = "jira_test4_results_template_raw.json";
            if (!save(jira_json, json_path))
                goto cleanup;
            g_free(jira_json);
            if (!build_new_jira_issue_file(
                json_path, summary, description, &jira_json))
                goto cleanup;
            json_path = "jira_test4_results_template_resolved.json";
            if (!save(jira_json, json_path))
                goto cleanup;
            if (!add_new_jira_issue(jira_json))
                goto cleanup;
            g_free(jira_json);
            if (!build_search_jira_issues(&jira_json))
                 goto cleanup;
            if (!search_jira_issues(jira_json, &jira_issues_json))
                goto cleanup;
            if (!parse_jira_issues(jira_issues_json, &jira_issues_list))
                goto cleanup;
            show_jira_issues_list(jira_issues_list);
            if (!save_jira_issues_csv(
                jira_issues_list,"jira_test4_results_add.csv"))
                goto cleanup;
            if (!save_jira_issues_xml(
                jira_issues_list,"jira_test4_results_add.xml"))
                goto cleanup;
            free_jira_issues_list(&jira_issues_list);
            break;
        case 5:
            summary =
            g_strdup_printf("CVE-%s (Test:5)", g_strstrip(ctime(&now)));
            if (!build_search_jira_issues(&jira_json)) {
                return false;
            }
            if (!get_jira_issues(jira_json, &jira_issues)) {
                return false;
            }
            show_jira_issues(jira_issues);
            free_jira_issues(&jira_issues);
            break;
        default:
            show_usage(argv[0]);
            printf("Error: Unknown test case: %i\n", test_case);
            break;
    }
    printf("Test passed!\n\n");
    destroy_jira_plugin();
    return 0;
cleanup:
    printf("Test failed!\n\n");
    destroy_jira_plugin();
    return 1;
}
コード例 #12
0
ファイル: rpm.c プロジェクト: fenrus75/cve-check-tool
struct source_package_t *rpm_inspect_spec(const char *filename)
{
        struct source_package_t *t = NULL;
        autofree(GFile) *fi = g_file_new_for_path(filename);
        autofree(GError) *error = NULL;
        if (!fi) {
                return NULL;
        }
        autofree(GFileInputStream) *fis = g_file_read(fi, NULL, &error);
        if (error) {
                g_printerr("Unable to read: %s\n", error->message);
                return NULL;
        }

        autofree(GDataInputStream) *dis = g_data_input_stream_new(G_INPUT_STREAM(fis));
        char *read = NULL;
        autofree(gchar) *name = NULL;
        autofree(gchar) *version = NULL;
        autofree(gchar) *release = NULL;
        autofree(GHashTable) *macros = NULL;
        autofree(gchar) *fpath = NULL;

        while ((read = g_data_input_stream_read_line(dis, NULL, NULL, NULL)) != NULL) {
                autofree(gstrv) *strv = NULL;
                const gchar *key = NULL;
                autofree(gchar) *value = NULL;

                read = g_strstrip(read);

                if (g_str_has_prefix(read, "%define") || g_str_has_prefix(read, "%global")) {
                        strv = g_strsplit(read, " ", 3);
                        if (g_strv_length(strv) != 3) {
                                goto clean;
                        }

                        gchar *mkey = g_strstrip(g_strdup_printf("%%{%s}", strv[1]));
                        gchar *mvalue = g_strstrip(g_strdup(strv[2]));

                        if (!macros) {
                                macros = g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &g_free);
                        }

                        if (!g_hash_table_contains(macros, mkey)) {
                                g_hash_table_insert(macros, mkey, mvalue);
                        } else {
                                g_free(mkey);
                                g_free(mvalue);
                        }
                        goto clean;
                }

                if (!strchr(read, ':')) {
                        goto clean;
                }

                strv = g_strsplit(read, ":", -1);
                if (g_strv_length(strv) < 2) {
                        goto clean;
                }
                key = g_strstrip(strv[0]);
                value = g_strjoinv(":", strv+1);
                value = g_strstrip(value);

                if (g_str_equal(key, "Name")) {
                        name = g_strdup(value);
                } else if (g_str_equal(key, "Version")) {
                        version = g_strdup(value);
                } else if (g_str_equal(key, "Release")) {
                        release = g_strdup(value);
                }

                if (name && version && release) {
                        g_free(read);
                        break;
                }
clean:
                g_free(read);
        }

        if (!name || !version || !release) {
                return NULL;
        }

        fpath = g_path_get_dirname(filename);
        if (!fpath) {
                return NULL;
        }

        if (macros) {
                name = demacro(macros, name);
                version = demacro(macros, version);
                release = demacro(macros, release);
        }

        t = calloc(1, sizeof(struct source_package_t));
        if (!t) {
                return NULL;
        }
        t->name = g_strdup(name);
        t->version = g_strdup(version);
        t->release = atoi(release);
        t->path = get_absolute_path(fpath);

        return t;
}