/**
- * Schedule the download of the specified
- * block in the tree.
+ * Schedule the download of the specified block in the tree.
*
* @param dc overall download this block belongs to
* @param chk content-hash-key of the block
}
+
+/**
+ * Suggest a filename based on given metadata.
+ *
+ * @param md given meta data
+ * @return NULL if meta data is useless for suggesting a filename
+ */
+char *
+GNUNET_FS_meta_data_suggest_filename (const struct GNUNET_CONTAINER_MetaData *md)
+{
+ static const char *mimeMap[][2] = {
+ {"application/bz2", ".bz2"},
+ {"application/gnunet-directory", ".gnd"},
+ {"application/java", ".class"},
+ {"application/msword", ".doc"},
+ {"application/ogg", ".ogg"},
+ {"application/pdf", ".pdf"},
+ {"application/pgp-keys", ".key"},
+ {"application/pgp-signature", ".pgp"},
+ {"application/postscript", ".ps"},
+ {"application/rar", ".rar"},
+ {"application/rtf", ".rtf"},
+ {"application/xml", ".xml"},
+ {"application/x-debian-package", ".deb"},
+ {"application/x-dvi", ".dvi"},
+ {"applixation/x-flac", ".flac"},
+ {"applixation/x-gzip", ".gz"},
+ {"application/x-java-archive", ".jar"},
+ {"application/x-java-vm", ".class"},
+ {"application/x-python-code", ".pyc"},
+ {"application/x-redhat-package-manager", ".rpm"},
+ {"application/x-rpm", ".rpm"},
+ {"application/x-tar", ".tar"},
+ {"application/x-tex-pk", ".pk"},
+ {"application/x-texinfo", ".texinfo"},
+ {"application/x-xcf", ".xcf"},
+ {"application/x-xfig", ".xfig"},
+ {"application/zip", ".zip"},
+
+ {"audio/midi", ".midi"},
+ {"audio/mpeg", ".mp3"},
+ {"audio/real", ".rm"},
+ {"audio/x-wav", ".wav"},
+
+ {"image/gif", ".gif"},
+ {"image/jpeg", ".jpg"},
+ {"image/pcx", ".pcx"},
+ {"image/png", ".png"},
+ {"image/tiff", ".tiff"},
+ {"image/x-ms-bmp", ".bmp"},
+ {"image/x-xpixmap", ".xpm"},
+
+ {"text/css", ".css"},
+ {"text/html", ".html"},
+ {"text/plain", ".txt"},
+ {"text/rtf", ".rtf"},
+ {"text/x-c++hdr", ".h++"},
+ {"text/x-c++src", ".c++"},
+ {"text/x-chdr", ".h"},
+ {"text/x-csrc", ".c"},
+ {"text/x-java", ".java"},
+ {"text/x-moc", ".moc"},
+ {"text/x-pascal", ".pas"},
+ {"text/x-perl", ".pl"},
+ {"text/x-python", ".py"},
+ {"text/x-tex", ".tex"},
+
+ {"video/avi", ".avi"},
+ {"video/mpeg", ".mpeg"},
+ {"video/quicktime", ".qt"},
+ {"video/real", ".rm"},
+ {"video/x-msvideo", ".avi"},
+ {NULL, NULL},
+ };
+ char *ret;
+ unsigned int i;
+ char *mime;
+ char *base;
+ const char *ext;
+
+ ret = GNUNET_CONTAINER_meta_data_get_by_type (md,
+ EXTRACTOR_METATYPE_FILENAME);
+ if (ret != NULL)
+ return ret;
+ ext = NULL;
+ mime = GNUNET_CONTAINER_meta_data_get_by_type (md,
+ EXTRACTOR_METATYPE_MIMETYPE);
+ if (mime != NULL)
+ {
+ i = 0;
+ while ( (mimeMap[i][0] != NULL) &&
+ (0 != strcmp (mime, mimeMap[i][0])))
+ i++;
+ if (mimeMap[i][1] == NULL)
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG |
+ GNUNET_ERROR_TYPE_BULK,
+ _("Did not find mime type `%s' in extension list.\n"),
+ mime);
+ else
+ ext = mimeMap[i][1];
+ GNUNET_free (mime);
+ }
+ base = GNUNET_CONTAINER_meta_data_get_first_by_types (md,
+ EXTRACTOR_METATYPE_TITLE,
+ EXTRACTOR_METATYPE_BOOK_TITLE,
+ EXTRACTOR_METATYPE_ORIGINAL_TITLE,
+ EXTRACTOR_METATYPE_PACKAGE_NAME,
+ EXTRACTOR_METATYPE_URL,
+ EXTRACTOR_METATYPE_URI,
+ EXTRACTOR_METATYPE_DESCRIPTION,
+ EXTRACTOR_METATYPE_ISRC,
+ EXTRACTOR_METATYPE_JOURNAL_NAME,
+ EXTRACTOR_METATYPE_AUTHOR_NAME,
+ EXTRACTOR_METATYPE_SUBJECT,
+ EXTRACTOR_METATYPE_ALBUM,
+ EXTRACTOR_METATYPE_ARTIST,
+ EXTRACTOR_METATYPE_KEYWORDS,
+ EXTRACTOR_METATYPE_COMMENT,
+ EXTRACTOR_METATYPE_UNKNOWN,
+ -1);
+ if ( (base == NULL) &&
+ (ext == NULL) )
+ return NULL;
+ if (base == NULL)
+ return GNUNET_strdup (ext);
+ if (ext == NULL)
+ return base;
+ GNUNET_asprintf (&ret,
+ "%s%s",
+ base,
+ ext);
+ GNUNET_free (base);
+ return ret;
+}
+
+
/**
* We've lost our connection with the FS service.
* Re-establish it and re-transmit all of our
{
struct GNUNET_FS_DownloadContext *dc = cls;
struct GNUNET_FS_DownloadContext *cpos;
+ struct GNUNET_DISK_FileHandle *fh;
+ char *fn;
+ char *us;
+ char *ext;
+ char *dn;
+ char *full_name;
if (NULL == uri)
return; /* entry for the directory itself */
}
if (cpos != NULL)
return; /* already exists */
+ fn = NULL;
if (NULL == filename)
{
-
+ fn = GNUNET_FS_meta_data_suggest_filename (meta);
+ if (fn == NULL)
+ {
+ us = GNUNET_FS_uri_to_string (uri);
+ fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_PREFIX
+ GNUNET_FS_URI_CHK_INFIX)]);
+ GNUNET_free (us);
+ }
+ else if (fn[0] == '.')
+ {
+ ext = fn;
+ us = GNUNET_FS_uri_to_string (uri);
+ GNUNET_asprintf (&fn,
+ "%s%s",
+ &us[strlen (GNUNET_FS_URI_PREFIX
+ GNUNET_FS_URI_CHK_INFIX)], ext);
+ GNUNET_free (ext);
+ GNUNET_free (us);
+ }
+ filename = fn;
+ }
+ if (dc->filename == NULL)
+ {
+ full_name = NULL;
+ }
+ else
+ {
+ dn = GNUNET_strdup (dc->filename);
+ GNUNET_break ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
+ (NULL !=
+ strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
+ GNUNET_FS_DIRECTORY_EXT)) );
+ if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
+ (NULL !=
+ strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
+ GNUNET_FS_DIRECTORY_EXT)) )
+ dn[strlen(dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';
+ if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
+ ( (strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
+ (NULL ==
+ strstr (filename + strlen(filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
+ GNUNET_FS_DIRECTORY_EXT)) ) )
+ {
+ GNUNET_asprintf (&full_name,
+ "%s%s%s%s",
+ dn,
+ DIR_SEPARATOR_STR,
+ filename,
+ GNUNET_FS_DIRECTORY_EXT);
+ }
+ else
+ {
+ GNUNET_asprintf (&full_name,
+ "%s%s%s",
+ dn,
+ DIR_SEPARATOR_STR,
+ filename);
+ }
+ GNUNET_free (dn);
+ }
+ if ( (full_name != NULL) &&
+ (GNUNET_OK !=
+ GNUNET_DISK_directory_create_for_file (full_name)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to create directory for recursive download of `%s'\n"),
+ full_name);
+ GNUNET_free (full_name);
+ GNUNET_free_non_null (fn);
+ return;
}
- if (data != NULL)
+
+ if (data != NULL)
{
- /* determine on-disk filename, write data! */
- GNUNET_break (0); // FIXME: not implemented
+ if (full_name != NULL)
+ {
+ fh = GNUNET_DISK_file_open (full_name,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE,
+ GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
+ if (fh == NULL)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ full_name);
+ GNUNET_free (full_name);
+ GNUNET_free_non_null (fn);
+ return;
+ }
+ if (length !=
+ GNUNET_DISK_file_write (fh,
+ data,
+ length))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "write",
+ full_name);
+ }
+ GNUNET_DISK_file_close (fh);
+ }
+ else
+ {
+ /* FIXME: generate 'progress' events and move to
+ instant completion! */
+ GNUNET_break (0); // FIXME: not implemented
+ }
}
- /* FIXME: filename MAY be NULL => make one up! */
GNUNET_FS_download_start (dc->h,
uri,
meta,
- filename, /* FIXME: prepend directory name! */
+ full_name,
0,
GNUNET_FS_uri_chk_get_file_size (uri),
dc->anonymity,
dc->options,
NULL,
dc);
+ GNUNET_free_non_null (full_name);
+ GNUNET_free_non_null (fn);
}
}
+/**
+ * Check if all child-downloads have completed and
+ * if so, signal completion (and possibly recurse to
+ * parent).
+ */
+static void
+check_completed (struct GNUNET_FS_DownloadContext *dc)
+{
+ struct GNUNET_FS_ProgressInfo pi;
+ struct GNUNET_FS_DownloadContext *pos;
+
+ pos = dc->child_head;
+ while (pos != NULL)
+ {
+ if ( (pos->emsg == NULL) &&
+ (pos->completed < pos->length) )
+ return; /* not done yet */
+ if ( (pos->child_head != NULL) &&
+ (pos->has_finished != GNUNET_YES) )
+ return; /* not transitively done yet */
+ pos = pos->next;
+ }
+ dc->has_finished = GNUNET_YES;
+ /* signal completion */
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
+ make_download_status (&pi, dc);
+ dc->client_info = dc->h->upcb (dc->h->upcb_cls,
+ &pi);
+ if (dc->parent != NULL)
+ check_completed (dc->parent);
+}
+
+
/**
* Iterator over entries in the pending requests in the 'active' map for the
* reply that we just got.
}
dc->completed += app;
+ /* do recursive download if option is set and either meta data
+ says it is a directory or if no meta data is given AND filename
+ ends in '.gnd' (top-level case) */
if ( (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
- (GNUNET_NO != GNUNET_FS_meta_data_test_for_directory (dc->meta)) )
- {
- GNUNET_FS_directory_list_contents (prc->size,
- pt,
- off,
- &trigger_recursive_download,
- dc);
- }
-
+ ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
+ ( (dc->meta == NULL) &&
+ ( (NULL == dc->filename) ||
+ ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
+ (NULL !=
+ strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
+ GNUNET_FS_DIRECTORY_EXT)) ) ) ) ) )
+ GNUNET_FS_directory_list_contents (prc->size,
+ pt,
+ off,
+ &trigger_recursive_download,
+ dc);
+
}
-
pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
make_download_status (&pi, dc);
pi.value.download.specifics.progress.data = pt;
}
if ( (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
- (GNUNET_NO != GNUNET_FS_meta_data_test_for_directory (dc->meta)) )
+ ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
+ ( (dc->meta == NULL) &&
+ ( (NULL == dc->filename) ||
+ ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
+ (NULL !=
+ strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
+ GNUNET_FS_DIRECTORY_EXT)) ) ) ) ) )
full_recursive_download (dc);
if (dc->child_head == NULL)
{
make_download_status (&pi, dc);
dc->client_info = dc->h->upcb (dc->h->upcb_cls,
&pi);
+ if (dc->parent != NULL)
+ check_completed (dc->parent);
}
GNUNET_assert (sm->depth == dc->treedepth);
}
GNUNET_CONTAINER_meta_data_destroy (dc->meta);
GNUNET_FS_uri_destroy (dc->uri);
GNUNET_free (dc->filename);
- GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
GNUNET_free (dc);
return NULL;
}
dc->length = length;
dc->anonymity = anonymity;
dc->options = options;
- dc->active = GNUNET_CONTAINER_multihashmap_create (2 * (length / DBLOCK_SIZE));
+ dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
#if DEBUG_DOWNLOAD
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,