/* This file is part of GNUnet. (C) 2001, 2002, 2003, 2004, 2005 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file applications/afs/tools/gnunet-insert.c * @brief Tool to insert or index files into GNUnet's AFS. * @author Christian Grothoff * @author Krista Bennett * @author James Blackwell * @author Igor Wronsky */ #include "gnunet_afs_esed2.h" #include "platform.h" #if USE_LIBEXTRACTOR #include #endif /* hmm. Man says time.h, but that doesn't yield the prototype. Strange... */ extern char *strptime(const char *s, const char *format, struct tm *tm); /** * Print progess message. */ static void printstatus(ProgressStats * stats, void * verboselevel) { if (*(int*)verboselevel == YES) { printf(_("%8u of %8u bytes inserted"), (unsigned int) stats->progress, (unsigned int) stats->filesize); printf("\r"); } } /** * Prints the usage information for this command if the user errs. * Aborts the program. */ static void printhelp() { static Help help[] = { { 'b', "builddir", NULL, gettext_noop("build a directory listing all processed files") }, HELP_CONFIG, { 'D', "desc", gettext_noop("DESCRIPTION"), gettext_noop("set description for all files") }, { 'e', "sprev", "FILENAME", gettext_noop("filename of the SBlock of a previous version of the content" " (for namespace insertions only)") }, { 'E', "extract", NULL, gettext_noop("print list of extracted keywords that would be used, but do not perform insertion or indexing") }, { 'f', "name", "NAME", gettext_noop("publish NAME as the name of the file or directory") }, HELP_HELP, HELP_HOSTNAME, { 'i', "interval", "SECONDS", gettext_noop("set interval for availability of updates to SECONDS" " (for namespace insertions only)") }, { 'k', "key", "KEYWORD", gettext_noop("add an additional keyword for the top-level file or directory" " (this option can be specified multiple times)") }, { 'K', "global-key", "KEYWORD", gettext_noop("add an additional keyword for all files and directories" " (this option can be specified multiple times)") }, { 'l', "link", NULL, gettext_noop("if gnunetd is running on the local machine, create a link instead of making a copy in the GNUnet share directory") }, HELP_LOGLEVEL, { 'm', "mime", "MIMETYPE", gettext_noop("set the mimetype for the file to be MIMETYPE") }, { 'n', "noindex", NULL, gettext_noop("do not index, perform full insertion (stores entire " "file in encrypted form in GNUnet database)") }, { 'N', "next", "ID", gettext_noop("specify ID of an updated version to be published in the future" " (for namespace insertions only)") }, { 'o', "sout", "FILENAME", gettext_noop("write the created SBlock in plaintext to FILENAME" " (for namespace insertions only)") }, { 'p', "prio", "PRIORITY", gettext_noop("specify the priority of the content") }, { 'P', "pass", "PASSWORD", gettext_noop("use PASSWORD to decrypt the secret key of the pseudonym" " (for namespace insertions only)") }, { 'R', "recursive", NULL, gettext_noop("process directories recursively") }, { 's', "pseudonym", "NAME", gettext_noop("publish the files under the pseudonym NAME (place file into namespace)") }, { 'S', "sporadic", NULL, gettext_noop("specifies this as an aperiodic but updated publication" " (for namespace insertions only)") }, { 't', "this", "ID", gettext_noop("set the ID of this version of the publication" " (for namespace insertions only)") }, { 'T', "time", "TIME", gettext_noop("specify creation time for SBlock (see man-page for format)") }, { 'u', "url", NULL, gettext_noop("print the GNUnet URL of the inserted file(s)") }, HELP_VERSION, HELP_VERBOSE, { 'x', "noextraction", NULL, gettext_noop("disable automatic metadata extraction") }, { 'X', "nodirectindex", NULL, gettext_noop("disable generation of RBlocks for keywords extracted from each file") }, HELP_END, }; formatHelp("gnunet-insert [OPTIONS] FILENAME*", _("Make files available to GNUnet for sharing."), help); } /** * Insert a single file. * * @param filename the name of the file to insert * @param fid resulting file identifier for the node * @returns OK on success, SYSERR on error */ static int doFile(GNUNET_TCP_SOCKET * sock, char * filename, FileIdentifier * fid, int * verbose) { Block * top; cron_t startTime; cronTime(&startTime); if (YES == *verbose) printf(_("Working on file '%s'.\n"), filename); top = insertFile(sock, filename, &printstatus, verbose); if (top == NULL) { printf(_("Error inserting file '%s'.\n" "You may want to check whether or not you are out of space.\n" "Run gnunet-stats | grep \"AFS storage left\" to check.\n"), filename); return SYSERR; } else { memcpy(&fid->chk, &top->chk, sizeof(CHK_Hashes)); fid->crc = htonl(crc32N(top->data, top->len)); fid->file_length = htonl(top->filesize); if (testConfigurationString("GNUNET-INSERT", "PRINTURL", "YES")) { char * fstring; fstring = createFileURI(fid); printf("%s\n", fstring); FREE(fstring); } if (*verbose == YES) { char * fstring; fstring = createFileURI(fid); printf(_("File '%s' successfully indexed -- %s\n"), filename, fstring); printf(_("Speed was %8.3f kilobyte per second.\n"), (top->filesize/1024.0) / (((double)(cronTime(NULL)-startTime)) / (double)cronSECONDS) ); FREE(fstring); } top->vtbl->done(top, NULL); return OK; } } static char ** topKeywords = NULL; int topKeywordCnt = 0; static char ** gloKeywords = NULL; int gloKeywordCnt = 0; static int parseOptions(int argc, char ** argv) { int c; int printAndReturn = NO; FREENONNULL(setConfigurationString("GNUNET-INSERT", "INDEX-CONTENT", "YES")); while (1) { int option_index=0; static struct GNoption long_options[] = { LONG_DEFAULT_OPTIONS, { "builddir", 0, 0, 'b' }, { "sprev", 1, 0, 'e' }, { "desc", 1, 0, 'D' }, { "sporadic", 0, 0, 'S' }, { "name", 1, 0, 'f' }, { "interval", 1, 0, 'i' }, { "extract", 0, 0, 'E' }, { "link", 0, 0, 'l' }, { "global-key", 1, 0, 'K' }, { "key", 1, 0, 'k' }, { "mime", 1, 0, 'm' }, { "noindex", 0, 0, 'n' }, { "next", 1, 0, 'N' }, { "sout", 1, 0, 'o' }, { "prio", 1, 0, 'p' }, { "pass", 1, 0, 'P' }, { "recursive", 0, 0, 'R' }, { "pseudonym", 1, 0, 's' }, { "this", 1, 0, 't' }, { "time", 1, 0, 'T' }, { "url", 0, 0, 'u' }, { "verbose", 0, 0, 'V' }, { "noextraction", 1, 0, 'x' }, { "nodirectindex", 1, 0, 'X' }, { 0,0,0,0 } }; c = GNgetopt_long(argc, argv, "bc:dD:e:Ef:hH:i:lL:k:K:m:nN:o:p:P:Rs:St:T:uvVxX", long_options, &option_index); if (c == -1) break; /* No more flags to process */ if (YES == parseDefaultOptions(c, GNoptarg)) continue; switch(c) { case 'b': FREENONNULL(setConfigurationString("GNUNET-INSERT", "BUILDDIR", "YES")); break; case 'e': FREENONNULL(setConfigurationString("GNUNET-INSERT", "PREVIOUS_SBLOCK", GNoptarg)); break; case 'D': FREENONNULL(setConfigurationString("GNUNET-INSERT", "DESCRIPTION", GNoptarg)); break; case 'E': printAndReturn = YES; break; case 'f': { char * root; FREENONNULL(setConfigurationString("GNUNET-INSERT", "FILENAME", GNoptarg)); root = getConfigurationString("GNUNET-INSERT", "FILENAMEROOT"); if (root == NULL) { /* if filename is '/home/user/foo', use 'foo' as the filenameRoot */ unsigned int i; root = GNoptarg; for (i=0;i 1) && (! testConfigurationString("GNUNET-INSERT", "BUILDDIR", "YES")) ) errexit(_("Cannot insert multiple files into namespace in one pass without using directory.")); timestr = getConfigurationString("GNUNET-INSERT", "INSERTTIME"); if (timestr != NULL) { struct tm t; if ((NULL == strptime(timestr, "%j-%m-%Y %R", &t))) { LOG_STRERROR(LOG_FATAL, "strptime"); errexit(_("Parsing time failed. Use 'DD-MM-YY HH:MM' format.\n")); } FREE(timestr); } prevname = getConfigurationString("GNUNET-INSERT", "PREVIOUS_SBLOCK"); if (prevname != NULL) { /* options from the previous sblock override */ PublicKey pkey; if (sizeof(SBlock) != readFile(prevname, sizeof(SBlock), &pb) ) { errexit(_("SBlock in file '%s' either does not exist or is malformed.\n"), prevname); } /* check that it matches the selected pseudonym */ getPublicKey(pseudonym, &pkey); if (0 != memcmp(&pkey, &pb.subspace, sizeof(PublicKey))) errexit(_("The given SBlock does not belong to the namespace of the selected pseudonym.")); if (SYSERR == verifySBlock(&pb)) { errexit(_("Verification of SBlock in file '%s' failed\n"), prevname); } FREE(prevname); interval = ntohl(pb.updateInterval); if (interval == SBLOCK_UPDATE_NONE) errexit(_("Trying to update nonupdatable SBlock.\n")); } } #if USE_LIBEXTRACTOR if (testConfigurationString("GNUNET-INSERT", "EXTRACT-KEYWORDS", "NO") && testConfigurationString("GNUNET-INSERT", "ADDITIONAL-RBLOCKS", "NO") ) printf(_("Option '%s' is implied by option '%s'.\n"), "-X", "-x"); extractors = (testConfigurationString("GNUNET-INSERT", "EXTRACT-KEYWORDS", "NO")) ? NULL : getExtractors(); #endif /* fixme: other sanity checks here? */ /* fundamental init */ sock = getClientSocket(); if (sock == NULL) errexit(_("Could not connect to gnunetd.\n")); /* first insert all of the top-level files or directories */ roots = MALLOC(sizeof(RootNode) * fileNameCount); skip = 0; /* number of failed insertions... */ for (i=0;i 1 || isDirectory(fileNames[0])) && testConfigurationString("GNUNET-INSERT", "BUILDDIR", "YES") ) { fileName = getConfigurationString("GNUNET-INSERT", "FILENAMEROOT"); if (fileName == NULL) fileName = STRDUP(_("no filename specified")); i = insertDirectory(sock, fileNameCount, roots, fileName, &fid, &printstatus, &verbose); for (j=0;j 1) && (shortFN != NULL) && (i == 0) ) { LOG(LOG_WARNING, _("Filename (option '%s') specified but multiple files given on the command line and not building directory. Will use the same filename for multiple files."), "-f"); } if ( (shortFN == NULL) && (fileName != NULL)) { shortFN = &fileName[strlen(fileName)-1]; while ( (shortFN[-1] != DIR_SEPARATOR) && (shortFN != fileName) ) shortFN--; shortFN = STRDUP(shortFN); } } else { shortFN = &fileName[strlen(fileName)-1]; while ( (shortFN[-1] != DIR_SEPARATOR) && (shortFN != fileName) ) shortFN--; shortFN = STRDUP(shortFN); } mimetype = getConfigurationString("GNUNET-INSERT", "MIMETYPE"); description = getConfigurationString("GNUNET-INSERT", "DESCRIPTION"); #if USE_LIBEXTRACTOR extractKeywordsMulti(fileName, &description, &mimetype, &keywords, &num_keywords, (testConfigurationString("GNUNET-INSERT", "EXTRACT-KEYWORDS", "NO")) ? NULL : extractors); #endif if (mimetype == NULL) mimetype = STRDUP("unknown"); if (description == NULL) description = STRDUP("No description supplied."); r = createRootNode(&roots[i].header.fileIdentifier, description, shortFN, mimetype); /* if a directory, add mimetype as key unless forbidden */ if ( (! testConfigurationString("GNUNET-INSERT", "ADDITIONAL-RBLOCKS", "NO")) && (0 != strcmp(mimetype, "unknown")) ) { insertRBlock(sock, r, mimetype); printf(_("Inserting file '%s' (%s, %s) under keyword '%s'.\n"), shortFN, description, mimetype, mimetype); } #if USE_LIBEXTRACTOR for (j=0;jsubspace, sizeof(PublicKey), &hc); if (OK == insertSBlock(sock, sb)) { char * outname; char * uri; outname = getConfigurationString("GNUNET-INSERT", "OUTPUT_SBLOCK"); if (outname != NULL) { SBlock plainSBlock; decryptSBlock(&thisId, sb, &plainSBlock); writeFile(outname, &plainSBlock, sizeof(SBlock), "600"); FREE(outname); } uri = createSubspaceURI(&hc, &thisId); printf(_("File '%s' (%s, %s) successfully inserted into namespace under\n" "\t'%s'\n"), shortFN, description, mimetype, uri); FREE(uri); } else { printf(_("Insertion of file into namespace failed.\n")); } FREE(sb); FREE(shortFN); } /* shutdown */ #if USE_LIBEXTRACTOR EXTRACTOR_removeAll(extractors); #endif for (i=0;i