fix
[oweals/gnunet.git] / src / fs / fs_collection.c
1 /*
2      This file is part of GNUnet.
3      (C) 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file fs/fs_collection.c
23  * @brief Helper functions for building a collection
24  * @author Christian Grothoff
25  *
26  * A collection is a special kind of namespace.  A collection is the
27  * set of files provided by the same user, but unlike namespaces it is
28  * automatically managed by the GNUnet UI.  A collection is a single
29  * directory in a namespace that is automatically updated each time
30  * the user updates or deletes a file.  That is, once the user starts
31  * a collection the gnunet-tools will always keep the corresponding
32  * directory and namespace entries up-to-date.
33  *
34  * A good way of thinking about a collection is a lazy user's
35  * namespace.
36  */
37
38 #include "platform.h"
39 #include "gnunet_directories.h"
40 #include "gnunet_fs_service.h"
41
42
43 /**
44  * Initialize collection.
45  *
46  * @param h handle to the file sharing subsystem
47  * @param namespace namespace to use for the collection
48  * @return GNUNET_OK on success, GNUNET_SYSERR if another
49  *         namespace is already set for our collection
50  */
51 int 
52 GNUNET_FS_collection_start (struct GNUNET_FS_Handle *h,
53                             struct GNUNET_FS_Namespace *namespace)
54 {
55   return GNUNET_SYSERR;
56 }
57
58
59 /**
60  * Stop collection.
61  *
62  * @param h handle to the file sharing subsystem
63  * @return GNUNET_OK on success, GNUNET_SYSERR if no collection is active
64  */
65 int 
66 GNUNET_FS_collection_stop (struct GNUNET_FS_Handle *h)
67 {
68   return GNUNET_SYSERR;
69 }
70
71
72 /**
73  * Are we using a collection?
74  *
75  * @param h handle to the file sharing subsystem
76  * @return NULL if there is no collection,
77  */
78 struct GNUNET_FS_Namespace *
79 GNUNET_FS_collection_get(struct GNUNET_FS_Handle *h)
80 {
81   return NULL;
82 }
83
84
85 /**
86  * Publish an update of the current collection information to the
87  * network now.  The function has no effect if the collection has not
88  * changed since the last publication.  If we are currently not
89  * collecting, this function does nothing.
90  *
91  * @param h handle to the file sharing subsystem
92  */
93 void GNUNET_FS_collection_publish (struct GNUNET_FS_Handle *h)
94 {
95 }
96
97
98 /**
99  * If we are currently building a collection, publish the given file
100  * information in that collection.  If we are currently not
101  * collecting, this function does nothing.
102  *
103  * @param h handle to the file sharing subsystem
104  * @param uri uri to add to the collection
105  * @param meta metadata for the uri
106  */
107 void GNUNET_FS_collection_add (const struct GNUNET_FS_Handle *h,
108                                const struct GNUNET_FS_Uri *uri,
109                                const struct GNUNET_CONTAINER_MetaData *meta)
110 {
111 }
112
113
114 #if 0
115
116 /**
117  * Filename used to store collection information
118  */
119 #define COLLECTION "collection"
120
121 #define COLLECTION_ROOT "root"
122
123 /**
124  * How long does a collection advertisement live?
125  */
126 #define COLLECTION_ADV_LIFETIME (12 * GNUNET_CRON_MONTHS)
127
128 /**
129  * @brief information about a collection
130  */
131 typedef struct CollectionData
132 {
133
134   /**
135    * What is the pseudonym ID for the publication?
136    */
137   GNUNET_HashCode pid;
138
139   /**
140    * Anonymity level for the collection. (NBO)
141    */
142   uint32_t anonymityLevel;
143
144   /**
145    * Priority of the collection (NBO).
146    */
147   uint32_t priority;
148
149   /**
150    * Has this collection changed since the last publication?
151    */
152   int changed;
153
154   /**
155    * Revision counter
156    */
157   unsigned int revision;
158
159 } CollectionData;
160
161
162 typedef struct
163 {
164
165   CollectionData data;
166
167   /**
168    * Metadata describing the collection
169    */
170   struct GNUNET_MetaData *meta;
171
172   /**
173    * Files in the collection.
174    */
175   GNUNET_ECRS_FileInfo *files;
176
177   /**
178    * How many files are in files?
179    */
180   unsigned int file_count;
181
182
183 } CollectionInfo;
184
185 static CollectionInfo *collectionData;
186
187 static struct GNUNET_Mutex *lock;
188
189 static struct GNUNET_GE_Context *ectx;
190
191 static struct GNUNET_GC_Configuration *cfg;
192
193 static char *
194 getCollectionFileName ()
195 {
196   return GNUNET_get_home_filename (ectx, cfg, GNUNET_NO, COLLECTION, NULL);
197 }
198
199 /**
200  * Initialize collection module.
201  */
202 void
203 GNUNET_CO_init (struct GNUNET_GE_Context *e,
204                 struct GNUNET_GC_Configuration *c)
205 {
206   char *fn;
207   int len;
208   unsigned int mlen;
209   unsigned long long size;
210   char *buf;
211   int fd;
212   const char *pos;
213   size_t rsize;
214   unsigned int i;
215   char *tmp;
216
217   cfg = c;
218   ectx = e;
219   lock = GNUNET_mutex_create (GNUNET_YES);
220   fn = getCollectionFileName ();
221   if (!GNUNET_disk_file_test (ectx, fn))
222     {
223       GNUNET_free (fn);
224       return;
225     }
226   /* read collection data */
227   if (GNUNET_OK != GNUNET_disk_file_size (ectx, fn, &size, GNUNET_YES))
228     {
229       GNUNET_free (fn);
230       return;
231     }
232   if ((size > 0x7FFFFFFF) ||
233       (size < sizeof (CollectionData) + 2 * sizeof (int)))
234     {
235       GNUNET_GE_BREAK (ectx, 0);
236       UNLINK (fn);
237       GNUNET_free (fn);
238       return;
239     }
240   fd = OPEN (fn, O_RDONLY | O_LARGEFILE);
241   if (fd == -1)
242     {
243       GNUNET_GE_BREAK (ectx, 0);
244       UNLINK (fn);
245       GNUNET_free (fn);
246       return;
247     }
248   rsize = (size_t) size;
249   buf = MMAP (NULL, rsize, PROT_READ, MAP_SHARED, fd, 0);
250   if (buf == MAP_FAILED)
251     {
252       GNUNET_GE_LOG_STRERROR_FILE (ectx,
253                                    GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
254                                    GNUNET_GE_USER | GNUNET_GE_BULK, "mmap",
255                                    fn);
256       CLOSE (fd);
257       GNUNET_free (fn);
258       return;
259     }
260   collectionData = GNUNET_malloc (sizeof (CollectionInfo));
261   memset (collectionData, 0, sizeof (CollectionInfo));
262   memcpy (&collectionData->data, buf, sizeof (CollectionData));
263   pos = &buf[sizeof (CollectionData)];
264   rsize -= sizeof (CollectionData);
265   len = ntohl (*(int *) pos);
266   if (len > 1024 * 1024 * 4)
267     goto ERR;
268   pos += sizeof (int);
269   mlen = ntohl (*(unsigned int *) pos);
270   pos += sizeof (unsigned int);
271   rsize -= 2 * sizeof (int);
272   if (mlen > rsize)
273     goto ERR;
274   GNUNET_array_grow (collectionData->files, collectionData->file_count, len);
275   collectionData->meta = GNUNET_meta_data_deserialize (ectx, pos, mlen);
276   rsize -= mlen;
277   pos += mlen;
278   GNUNET_GE_BREAK (ectx, collectionData->meta != NULL);
279   for (i = 0; i < collectionData->file_count; i++)
280     {
281       if (rsize < 2 * sizeof (int))
282         {
283           GNUNET_GE_BREAK (ectx, 0);
284           break;
285         }
286       len = ntohl (*(int *) pos);
287       pos += sizeof (int);
288       mlen = ntohl (*(int *) pos);
289       pos += sizeof (int);
290       rsize -= 2 * sizeof (int);
291       if (rsize < mlen + len)
292         {
293           GNUNET_GE_BREAK (ectx, 0);
294           break;
295         }
296       if (len > 1024 * 16)
297         {
298           GNUNET_GE_BREAK (ectx, 0);
299           len = 1024 * 16;
300         }
301       tmp = GNUNET_malloc (len + 1);
302       tmp[len] = '\0';
303       memcpy (tmp, pos, len);
304       pos += len;
305       rsize -= len;
306       collectionData->files[i].uri = GNUNET_ECRS_string_to_uri (ectx, tmp);
307       GNUNET_GE_ASSERT (ectx, collectionData->files[i].uri != NULL);
308       GNUNET_free (tmp);
309       collectionData->files[i].meta
310         = GNUNET_meta_data_deserialize (ectx, pos, mlen);
311       GNUNET_GE_ASSERT (ectx, collectionData->files[i].meta != NULL);
312       pos += mlen;
313       rsize -= mlen;
314     }
315   GNUNET_GE_ASSERT (ectx, rsize == 0);
316   MUNMAP (buf, (size_t) size);
317   CLOSE (fd);
318   GNUNET_free (fn);
319   /* kill invalid entries (meta or uri == NULL) */
320   for (i = 0; i < collectionData->file_count; i++)
321     {
322       if ((collectionData->files[i].uri != NULL) &&
323           (collectionData->files[i].meta != NULL))
324         continue;
325       if (collectionData->files[i].uri != NULL)
326         GNUNET_ECRS_uri_destroy (collectionData->files[i].uri);
327       if (collectionData->files[i].meta != NULL)
328         GNUNET_meta_data_destroy (collectionData->files[i].meta);
329       collectionData->files[i]
330         = collectionData->files[collectionData->file_count - 1];
331       GNUNET_array_grow (collectionData->files,
332                          collectionData->file_count,
333                          collectionData->file_count - 1);
334     }
335   return;
336 ERR:
337   MUNMAP (buf, (size_t) size);
338   CLOSE (fd);
339   GNUNET_free (fn);
340   GNUNET_free (collectionData);
341   collectionData = NULL;
342 }
343
344 static void
345 WRITEINT (int fd, int val)
346 {
347   int bval;
348
349   bval = htonl (val);
350   WRITE (fd, &bval, sizeof (int));
351 }
352
353 static void
354 writeCO ()
355 {
356   char *fn;
357   unsigned int mlen;
358   char *buf;
359   int fd;
360   unsigned int i;
361   char *tmp;
362
363   if (collectionData == NULL)
364     return;
365
366   /* write collection data */
367   mlen =
368     GNUNET_meta_data_get_serialized_size (collectionData->meta, GNUNET_NO);
369   buf = GNUNET_malloc (mlen);
370   if (mlen != GNUNET_meta_data_serialize (ectx,
371                                           collectionData->meta, buf,
372                                           mlen, GNUNET_NO))
373     {
374       GNUNET_GE_BREAK (ectx, 0);
375       GNUNET_free (buf);
376       return;
377     }
378
379   fn = getCollectionFileName ();
380   fd = OPEN (fn,
381              O_CREAT | O_LARGEFILE | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
382   if (fd == -1)
383     {
384       GNUNET_GE_LOG_STRERROR_FILE (ectx,
385                                    GNUNET_GE_USER | GNUNET_GE_ADMIN |
386                                    GNUNET_GE_ERROR | GNUNET_GE_BULK, "open",
387                                    fn);
388       GNUNET_free (fn);
389       GNUNET_free (buf);
390       return;
391     }
392   GNUNET_GE_BREAK (ectx, collectionData->file_count <= 1024 * 1024 * 4);
393   WRITE (fd, collectionData, sizeof (CollectionData));
394   WRITEINT (fd, collectionData->file_count);
395   WRITEINT (fd, mlen);
396   WRITE (fd, buf, mlen);
397   GNUNET_free (buf);
398   for (i = 0; i < collectionData->file_count; i++)
399     {
400       mlen =
401         GNUNET_meta_data_get_serialized_size (collectionData->files[i].meta,
402                                               GNUNET_NO);
403       buf = GNUNET_malloc (mlen);
404       if (mlen != GNUNET_meta_data_serialize (ectx,
405                                               collectionData->files[i].meta,
406                                               buf, mlen, GNUNET_NO))
407         {
408           GNUNET_GE_BREAK (ectx, 0);
409           GNUNET_free (buf);
410           break;
411         }
412       tmp = GNUNET_ECRS_uri_to_string (collectionData->files[i].uri);
413       WRITEINT (fd, strlen (tmp));
414       WRITEINT (fd, mlen);
415       GNUNET_GE_BREAK (ectx, strlen (tmp) < 16 * 1024);
416       WRITE (fd, tmp, strlen (tmp));
417       GNUNET_free (tmp);
418       WRITE (fd, buf, mlen);
419       GNUNET_free (buf);
420     }
421   CLOSE (fd);
422   GNUNET_free (fn);
423 }
424
425 static void
426 free_collection_data ()
427 {
428   unsigned int i;
429
430   if (collectionData == NULL)
431     return;
432   GNUNET_meta_data_destroy (collectionData->meta);
433   for (i = 0; i < collectionData->file_count; i++)
434     {
435       GNUNET_meta_data_destroy (collectionData->files[i].meta);
436       GNUNET_ECRS_uri_destroy (collectionData->files[i].uri);
437     }
438   GNUNET_array_grow (collectionData->files, collectionData->file_count, 0);
439   GNUNET_free (collectionData);
440   collectionData = NULL;
441 }
442
443 /**
444  * Shutdown collection module.
445  */
446 void
447 GNUNET_CO_done ()
448 {
449   writeCO ();
450   free_collection_data ();
451   GNUNET_mutex_destroy (lock);
452   lock = NULL;
453   ectx = NULL;
454   cfg = NULL;
455 }
456
457
458 /**
459  * Start collection.
460  *
461  * @param updateInterval of GNUNET_ECRS_SBLOCK_UPDATE_NONE
462  *        means to update _immediately_ on any change,
463  *        wherease GNUNET_ECRS_SBLOCK_UPDATE_SPORADIC means
464  *        to publish updates when the CO_Context
465  *        is destroyed (i.e. on exit from the UI).
466  */
467 int
468 GNUNET_CO_collection_start (uint32_t anonymityLevel,
469                             uint32_t prio,
470                             const struct GNUNET_MetaData *meta)
471 {
472   struct GNUNET_ECRS_URI *advertisement;
473   struct GNUNET_ECRS_URI *rootURI;
474   const char *root = COLLECTION_ROOT;
475
476   GNUNET_mutex_lock (lock);
477   GNUNET_CO_collection_stop (); /* cancel old collection */
478   advertisement = GNUNET_ECRS_keyword_string_to_uri (ectx, COLLECTION);
479   GNUNET_GE_ASSERT (ectx, advertisement != NULL);
480   rootURI = GNUNET_ECRS_namespace_create (ectx,
481                                           cfg,
482                                           meta,
483                                           anonymityLevel,
484                                           prio,
485                                           GNUNET_get_time () +
486                                           COLLECTION_ADV_LIFETIME,
487                                           advertisement, root);
488   GNUNET_ECRS_uri_destroy (advertisement);
489   if (rootURI == NULL)
490     {
491       GNUNET_mutex_unlock (lock);
492       return GNUNET_SYSERR;
493     }
494   collectionData = GNUNET_malloc (sizeof (CollectionInfo));
495   memset (collectionData, 0, sizeof (CollectionInfo));
496   GNUNET_ECRS_uri_get_namespace_from_sks (rootURI, &collectionData->data.pid);
497   GNUNET_ECRS_uri_destroy (rootURI);
498   collectionData->data.priority = htonl (prio);
499   collectionData->data.anonymityLevel = htonl (anonymityLevel);
500   collectionData->meta = GNUNET_meta_data_duplicate (meta);
501   GNUNET_mutex_unlock (lock);
502   return GNUNET_OK;
503 }
504
505 /**
506  * Stop collection.
507  *
508  * @return GNUNET_OK on success, GNUNET_SYSERR if no collection is active
509  */
510 int
511 GNUNET_CO_collection_stop ()
512 {
513   GNUNET_mutex_lock (lock);
514   if (collectionData == NULL)
515     {
516       GNUNET_mutex_unlock (lock);
517       return GNUNET_SYSERR;
518     }
519   GNUNET_ECRS_namespace_delete (ectx, cfg, &collectionData->data.pid);
520   free_collection_data ();
521   GNUNET_mutex_unlock (lock);
522   return GNUNET_OK;
523 }
524
525 /**
526  * Are we using a collection?
527  *
528  * @return NULL if there is no collection, otherwise its metadata
529  */
530 struct GNUNET_MetaData *
531 GNUNET_CO_collection_get_name ()
532 {
533   struct GNUNET_MetaData *meta;
534
535   GNUNET_mutex_lock (lock);
536   if (collectionData == NULL)
537     {
538       GNUNET_mutex_unlock (lock);
539       return NULL;
540     }
541   meta = GNUNET_meta_data_duplicate (collectionData->meta);
542   GNUNET_mutex_unlock (lock);
543   return meta;
544 }
545
546 /**
547  * Publish an update of the current collection information to the
548  * network now.  The function has no effect if the collection has not
549  * changed since the last publication.  If we are currently not
550  * collecting, this function does nothing.
551  */
552 void
553 GNUNET_CO_collection_publish_now ()
554 {
555   struct GNUNET_ECRS_URI *uri;
556   struct GNUNET_ECRS_URI *directoryURI;
557   unsigned long long dirLen;
558   char *tmpName;
559   int fd;
560   char *dirData;
561   char this_revision_string[128];
562   char next_revision_string[128];
563   const char *tmpdir;
564
565   GNUNET_mutex_lock (lock);
566   if ((collectionData == NULL) ||
567       (ntohl (collectionData->data.changed) == GNUNET_NO))
568     {
569       GNUNET_mutex_unlock (lock);
570       return;
571     }
572
573   tmpdir = getenv ("TMPDIR");
574   tmpdir = tmpdir ? tmpdir : "/tmp";
575
576 #define TEMPLATE "/gnunet-collectionXXXXXX"
577   tmpName = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1);
578   strcpy (tmpName, tmpdir);
579   strcat (tmpName, TEMPLATE);
580 #undef TEMPLATE
581   fd = mkstemp (tmpName);
582   if (fd == -1)
583     {
584       GNUNET_GE_LOG_STRERROR (ectx,
585                               GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
586                               GNUNET_GE_BULK, "mkstemp");
587       GNUNET_free (tmpName);
588       GNUNET_mutex_unlock (lock);
589       return;
590     }
591   dirData = NULL;
592   GNUNET_GE_ASSERT (ectx,
593                     GNUNET_OK == GNUNET_ECRS_directory_create (ectx,
594                                                                &dirData,
595                                                                &dirLen,
596                                                                collectionData->file_count,
597                                                                collectionData->files,
598                                                                collectionData->meta));
599   if (-1 == WRITE (fd, dirData, dirLen))
600     {
601       GNUNET_GE_LOG_STRERROR (ectx,
602                               GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
603                               GNUNET_GE_BULK, "write");
604       GNUNET_free (tmpName);
605       GNUNET_free (dirData);
606       GNUNET_mutex_unlock (lock);
607       return;
608     }
609   GNUNET_free (dirData);
610   CLOSE (fd);
611   if (GNUNET_OK != GNUNET_ECRS_file_upload (ectx, cfg, tmpName, GNUNET_NO,      /* indexing */
612                                             ntohl (collectionData->
613                                                    data.anonymityLevel),
614                                             ntohl (collectionData->
615                                                    data.priority),
616                                             GNUNET_get_time () +
617                                             COLLECTION_ADV_LIFETIME, NULL,
618                                             NULL, NULL, NULL, &directoryURI))
619     {
620       UNLINK (tmpName);
621       GNUNET_free (tmpName);
622       GNUNET_mutex_unlock (lock);
623       return;
624     }
625   UNLINK (tmpName);
626   GNUNET_free (tmpName);
627   if (ntohl (collectionData->data.revision) == 0)
628     strcpy (this_revision_string, COLLECTION_ROOT);
629   else
630     GNUNET_snprintf (this_revision_string,
631                      sizeof (this_revision_string),
632                      _("Revision %u"), ntohl (collectionData->data.revision));
633   GNUNET_snprintf (next_revision_string,
634                    sizeof (next_revision_string),
635                    _("Revision %u"),
636                    ntohl (collectionData->data.revision) + 1);
637   uri = GNUNET_ECRS_namespace_add_content (ectx,
638                                            cfg,
639                                            &collectionData->data.pid,
640                                            ntohl (collectionData->
641                                                   data.anonymityLevel),
642                                            ntohl (collectionData->
643                                                   data.priority),
644                                            GNUNET_get_time () +
645                                            COLLECTION_ADV_LIFETIME,
646                                            this_revision_string,
647                                            next_revision_string, directoryURI,
648                                            collectionData->meta);
649   if (uri != NULL)
650     {
651       collectionData->data.revision =
652         htonl (ntohl (collectionData->data.revision) + 1);
653       collectionData->data.changed = htonl (GNUNET_NO);
654       GNUNET_ECRS_uri_destroy (uri);
655     }
656   GNUNET_mutex_unlock (lock);
657 }
658
659 /**
660  * If we are currently building a collection, publish
661  * the given file information in that collection.
662  * If we are currently not collecting, this function
663  * does nothing.
664  */
665 void
666 GNUNET_CO_collection_add_item (const GNUNET_ECRS_FileInfo * fi)
667 {
668   unsigned int i;
669   GNUNET_ECRS_FileInfo fc;
670
671   if ((GNUNET_ECRS_uri_test_ksk (fi->uri)))
672     {
673       GNUNET_GE_BREAK (ectx, 0);
674       return;
675     }
676   if (lock == NULL)
677     {
678       GNUNET_GE_BREAK (ectx, 0);
679       return;
680     }
681   GNUNET_mutex_lock (lock);
682   if (collectionData == NULL)
683     {
684       GNUNET_mutex_unlock (lock);
685       return;
686     }
687   for (i = 0; i < collectionData->file_count; i++)
688     {
689       if (GNUNET_ECRS_uri_test_equal (fi->uri, collectionData->files[i].uri))
690         {
691           GNUNET_mutex_unlock (lock);
692           return;
693         }
694     }
695   fc.uri = GNUNET_ECRS_uri_duplicate (fi->uri);
696   fc.meta = GNUNET_meta_data_duplicate (fi->meta);
697   GNUNET_array_append (collectionData->files, collectionData->file_count, fc);
698   collectionData->data.changed = htonl (GNUNET_YES);
699   GNUNET_mutex_unlock (lock);
700 }
701
702 #endif
703
704 /* end of fs_collection.c */