/** * Print an entry in a directory. * * @param cls closure (not used) * @param filename name of the file in the directory * @param uri URI of the file * @param meta metadata for the file; metadata for * the directory if everything else is NULL/zero * @param length length of the available data for the file * (of type size_t since data must certainly fit * into memory; if files are larger than size_t * permits, then they will certainly not be * embedded with the directory itself). * @param data data available for the file (length bytes) */ static void print_entry (void *cls, const char *filename, const struct GNUNET_FS_Uri *uri, const struct GNUNET_CONTAINER_MetaData *meta, size_t length, const void *data) { char *string; char *name; name = GNUNET_CONTAINER_meta_data_get_by_type (meta, EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); if (uri == NULL) { printf (_("Directory `%s' meta data:\n"), name); GNUNET_CONTAINER_meta_data_iterate (meta, &item_printer, NULL); printf ("\n"); printf (_("Directory `%s' contents:\n"), name); GNUNET_free (name); return; } string = GNUNET_FS_uri_to_string (uri); printf ("%s (%s):\n", name, string); GNUNET_free (string); GNUNET_CONTAINER_meta_data_iterate (meta, &item_printer, NULL); printf ("\n"); GNUNET_free (name); }
/** * Get the first matching MD entry of the given types. Caller is * responsible for freeing the return value. Also, only meta data * items that are strings (0-terminated) are returned by this * function. * * @param md metadata to inspect * @param ... -1-terminated list of types * @return NULL if we do not have any such entry, * otherwise client is responsible for freeing the value! */ char * GNUNET_CONTAINER_meta_data_get_first_by_types (const struct GNUNET_CONTAINER_MetaData *md, ...) { char *ret; va_list args; enum EXTRACTOR_MetaType type; if (md == NULL) return NULL; ret = NULL; va_start (args, md); while (1) { type = va_arg (args, enum EXTRACTOR_MetaType); if (type == -1) break; ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type); if (ret != NULL) break; } va_end (args); return ret; }
/** * Does the meta-data claim that this is a directory? * Checks if the mime-type is that of a GNUnet directory. * * @return #GNUNET_YES if it is, #GNUNET_NO if it is not, #GNUNET_SYSERR if * we have no mime-type information (treat as #GNUNET_NO) */ int GNUNET_FS_meta_data_test_for_directory (const struct GNUNET_CONTAINER_MetaData *md) { char *mime; int ret; if (NULL == md) return GNUNET_SYSERR; mime = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE); if (NULL == mime) return GNUNET_SYSERR; ret = (0 == strcasecmp (mime, GNUNET_FS_DIRECTORY_MIME)) ? GNUNET_YES : GNUNET_NO; GNUNET_free (mime); return ret; }
/** * Set the MIMETYPE information for the given * metadata to "application/gnunet-directory". * * @param md metadata to add mimetype to */ void GNUNET_FS_meta_data_make_directory (struct GNUNET_CONTAINER_MetaData *md) { char *mime; mime = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_MIMETYPE); if (mime != NULL) { GNUNET_break (0 == strcmp (mime, GNUNET_FS_DIRECTORY_MIME)); GNUNET_free (mime); return; } GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>", EXTRACTOR_METATYPE_MIMETYPE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", GNUNET_FS_DIRECTORY_MIME, strlen (GNUNET_FS_DIRECTORY_MIME) + 1); }
/** * Iterate over all entries in a directory. Note that directories * are structured such that it is possible to iterate over the * individual blocks as well as over the entire directory. Thus * a client can call this function on the buffer in the * GNUNET_FS_ProgressCallback. Also, directories can optionally * include the contents of (small) files embedded in the directory * itself; for those files, the processor may be given the * contents of the file directly by this function. * <p> * * Note that this function maybe called on parts of directories. Thus * parser errors should not be reported _at all_ (with GNUNET_break). * Still, if some entries can be recovered despite these parsing * errors, the function should try to do this. * * @param size number of bytes in data * @param data pointer to the beginning of the directory * @param offset offset of data in the directory * @param dep function to call on each entry * @param dep_cls closure for dep * @return GNUNET_OK if this could be a block in a directory, * GNUNET_NO if this could be part of a directory (but not 100% OK) * GNUNET_SYSERR if 'data' does not represent a directory */ int GNUNET_FS_directory_list_contents (size_t size, const void *data, uint64_t offset, GNUNET_FS_DirectoryEntryProcessor dep, void *dep_cls) { struct GetFullDataClosure full_data; const char *cdata = data; char *emsg; uint64_t pos; uint64_t align; uint32_t mdSize; uint64_t epos; struct GNUNET_FS_Uri *uri; struct GNUNET_CONTAINER_MetaData *md; char *filename; if ((offset == 0) && ((size < 8 + sizeof (uint32_t)) || (0 != memcmp (cdata, GNUNET_FS_DIRECTORY_MAGIC, 8)))) return GNUNET_SYSERR; pos = offset; if (offset == 0) { GNUNET_memcpy (&mdSize, &cdata[8], sizeof (uint32_t)); mdSize = ntohl (mdSize); if (mdSize > size - 8 - sizeof (uint32_t)) { /* invalid size */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("MAGIC mismatch. This is not a GNUnet directory.\n")); return GNUNET_SYSERR; } md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[8 + sizeof (uint32_t)], mdSize); if (md == NULL) { GNUNET_break (0); return GNUNET_SYSERR; /* malformed ! */ } dep (dep_cls, NULL, NULL, md, 0, NULL); GNUNET_CONTAINER_meta_data_destroy (md); pos = 8 + sizeof (uint32_t) + mdSize; } while (pos < size) { /* find end of URI */ if (cdata[pos] == '\0') { /* URI is never empty, must be end of block, * skip to next alignment */ align = ((pos / DBLOCK_SIZE) + 1) * DBLOCK_SIZE; if (align == pos) { /* if we were already aligned, still skip a block! */ align += DBLOCK_SIZE; } pos = align; if (pos >= size) { /* malformed - or partial download... */ break; } } epos = pos; while ((epos < size) && (cdata[epos] != '\0')) epos++; if (epos >= size) return GNUNET_NO; /* malformed - or partial download */ uri = GNUNET_FS_uri_parse (&cdata[pos], &emsg); pos = epos + 1; if (uri == NULL) { GNUNET_free (emsg); pos--; /* go back to '\0' to force going to next alignment */ continue; } if (GNUNET_FS_uri_test_ksk (uri)) { GNUNET_FS_uri_destroy (uri); GNUNET_break (0); return GNUNET_NO; /* illegal in directory! */ } GNUNET_memcpy (&mdSize, &cdata[pos], sizeof (uint32_t)); mdSize = ntohl (mdSize); pos += sizeof (uint32_t); if (pos + mdSize > size) { GNUNET_FS_uri_destroy (uri); return GNUNET_NO; /* malformed - or partial download */ } md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[pos], mdSize); if (md == NULL) { GNUNET_FS_uri_destroy (uri); GNUNET_break (0); return GNUNET_NO; /* malformed ! */ } pos += mdSize; filename = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); full_data.size = 0; full_data.data = NULL; GNUNET_CONTAINER_meta_data_iterate (md, &find_full_data, &full_data); if (dep != NULL) { dep (dep_cls, filename, uri, md, full_data.size, full_data.data); } GNUNET_free_non_null (full_data.data); GNUNET_free_non_null (filename); GNUNET_CONTAINER_meta_data_destroy (md); GNUNET_FS_uri_destroy (uri); } return GNUNET_OK; }
/** * Called by FS client to give information about the progress of an * operation. * * @param cls closure * @param info details about the event, specifying the event type * and various bits about the event * @return client-context (for the next progress call * for this operation; should be set to NULL for * SUSPEND and STOPPED events). The value returned * will be passed to future callbacks in the respective * field in the GNUNET_FS_ProgressInfo struct. */ static void * progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) { static unsigned int cnt; int is_directory; char *uri; char *filename; switch (info->status) { case GNUNET_FS_STATUS_SEARCH_START: break; case GNUNET_FS_STATUS_SEARCH_RESULT: if (db != NULL) GNUNET_FS_directory_builder_add (db, info->value.search.specifics.result.uri, info->value.search.specifics.result.meta, NULL); uri = GNUNET_FS_uri_to_string (info->value.search.specifics.result.uri); printf ("#%u:\n", cnt++); filename = GNUNET_CONTAINER_meta_data_get_by_type (info->value.search. specifics.result.meta, EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); is_directory = GNUNET_FS_meta_data_test_for_directory (info->value.search. specifics.result.meta); if (filename != NULL) { GNUNET_DISK_filename_canonicalize (filename); if (GNUNET_YES == is_directory) printf ("gnunet-download -o \"%s%s\" -R %s\n", filename, GNUNET_FS_DIRECTORY_EXT, uri); else printf ("gnunet-download -o \"%s\" %s\n", filename, uri); } else if (GNUNET_YES == is_directory) printf ("gnunet-download -o \"collection%s\" -R %s\n", GNUNET_FS_DIRECTORY_EXT, uri); else printf ("gnunet-download %s\n", uri); if (verbose) GNUNET_CONTAINER_meta_data_iterate (info->value.search.specifics. result.meta, &item_printer, NULL); printf ("\n"); fflush (stdout); GNUNET_free_non_null (filename); GNUNET_free (uri); results++; if ((results_limit > 0) && (results >= results_limit)) GNUNET_SCHEDULER_shutdown (); break; case GNUNET_FS_STATUS_SEARCH_UPDATE: break; case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: /* ignore */ break; case GNUNET_FS_STATUS_SEARCH_ERROR: FPRINTF (stderr, _("Error searching: %s.\n"), info->value.search.specifics.error.message); GNUNET_SCHEDULER_shutdown (); break; case GNUNET_FS_STATUS_SEARCH_STOPPED: GNUNET_SCHEDULER_add_now (&clean_task, NULL); break; default: FPRINTF (stderr, _("Unexpected status: %d\n"), info->status); break; } return NULL; }