55ed3884ad53ac6bb25e474d0344da3abaee0082
[oweals/gnunet.git] / src / util / pseudonym.c
1 /*
2      This file is part of GNUnet
3      (C) 2003, 2004, 2005, 2006, 2007, 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 util/pseudonym.c
23  * @brief helper functions
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_disk_lib.h"
31 #include "gnunet_pseudonym_lib.h"
32 #include "gnunet_bio_lib.h"
33
34 /** 
35  * FIXME
36  */
37 #define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/metadata" DIR_SEPARATOR_STR
38
39 /** 
40  * FIXME
41  */
42 #define PS_NAMES_DIR    DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/names"    DIR_SEPARATOR_STR
43
44
45 /** 
46  * FIXME
47  */
48 struct DiscoveryCallback
49 {
50
51   /** 
52    * FIXME
53    */
54   struct DiscoveryCallback *next;
55
56   /** 
57    * FIXME
58    */
59   GNUNET_PSEUDONYM_Iterator callback;
60
61   /** 
62    * FIXME
63    */
64   void *closure;
65 };
66
67
68 /** 
69  * FIXME
70  */
71 static struct DiscoveryCallback *head;
72
73 /**
74  * Internal notification about new tracked URI.
75  * 
76  * FIXME
77  */
78 static void
79 internal_notify (const GNUNET_HashCode * id,
80                  const struct GNUNET_CONTAINER_MetaData *md, int rating)
81 {
82   struct DiscoveryCallback *pos;
83
84   pos = head;
85   while (pos != NULL)
86     {
87       pos->callback (pos->closure, id, md, rating);
88       pos = pos->next;
89     }
90 }
91
92 /**
93  * Register callback to be invoked whenever we discover
94  * a new pseudonym.
95  * 
96  * FIXME
97  */
98 int
99 GNUNET_PSEUDONYM_discovery_callback_register (const struct
100                                               GNUNET_CONFIGURATION_Handle
101                                               *cfg,
102                                               GNUNET_PSEUDONYM_Iterator
103                                               iterator, void *closure)
104 {
105   struct DiscoveryCallback *list;
106
107   list = GNUNET_malloc (sizeof (struct DiscoveryCallback));
108   list->callback = iterator;
109   list->closure = closure;
110   list->next = head;
111   head = list;
112   GNUNET_PSEUDONYM_list_all (cfg, iterator, closure);
113   return GNUNET_OK;
114 }
115
116 /**
117  * Unregister pseudonym discovery callback.
118  * 
119  * FIXME
120  */
121 int
122 GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
123                                                 iterator, void *closure)
124 {
125   struct DiscoveryCallback *prev;
126   struct DiscoveryCallback *pos;
127
128   prev = NULL;
129   pos = head;
130   while ((pos != NULL) &&
131          ((pos->callback != iterator) || (pos->closure != closure)))
132     {
133       prev = pos;
134       pos = pos->next;
135     }
136   if (pos == NULL)
137     return GNUNET_SYSERR;
138   if (prev == NULL)
139     head = pos->next;
140   else
141     prev->next = pos->next;
142   GNUNET_free (pos);
143   return GNUNET_OK;
144 }
145
146
147 /**
148  * Get the filename (or directory name) for the given
149  * pseudonym identifier and directory prefix.
150  * 
151  * FIXME
152  */
153 static char *
154 get_data_filename (const struct GNUNET_CONFIGURATION_Handle
155                    *cfg, const char *prefix, const GNUNET_HashCode * psid)
156 {
157   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
158
159   if (psid != NULL)
160     GNUNET_CRYPTO_hash_to_enc (psid, &enc);
161   return GNUNET_DISK_get_home_filename (cfg,
162                                         GNUNET_CLIENT_SERVICE_NAME,
163                                         prefix,
164                                         (psid ==
165                                          NULL) ? NULL : (const char *) &enc,
166                                         NULL);
167 }
168
169
170 /**
171  * FIXME
172  */
173 static void
174 write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
175                       const GNUNET_HashCode * nsid,
176                       const struct GNUNET_CONTAINER_MetaData *meta,
177                       int32_t ranking, const char *ns_name)
178 {
179   char *fn;
180   struct GNUNET_BIO_WriteHandle *fileW;
181
182   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
183   GNUNET_assert (fn != NULL);
184   fileW = GNUNET_BIO_write_open(fn);
185   if (NULL != fileW) 
186     {
187       if ( (GNUNET_OK != GNUNET_BIO_write_int32(fileW, ranking)) ||
188            (GNUNET_OK != GNUNET_BIO_write_string(fileW, ns_name)) ||
189            (GNUNET_OK != GNUNET_BIO_write_meta_data(fileW, meta)) )
190         {
191           GNUNET_BIO_write_close(fileW);
192           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
193           GNUNET_free (fn);
194           return;
195         }
196       if (GNUNET_OK != GNUNET_BIO_write_close(fileW))
197         {
198           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
199           GNUNET_free (fn);
200           return;
201         }
202     }
203   GNUNET_free (fn);
204   /* create entry for pseudonym name in names */
205   GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
206 }
207
208
209 /**
210  * FIXME
211  */
212 static int
213 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
214            const GNUNET_HashCode * nsid,
215            struct GNUNET_CONTAINER_MetaData **meta,
216            int32_t * ranking, char **ns_name)
217 {
218   char *fn;
219   char *emsg;
220   struct GNUNET_BIO_ReadHandle *fileR;
221
222   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
223   GNUNET_assert (fn != NULL);
224   fileR = GNUNET_BIO_read_open(fn);
225   if (fileR == NULL)
226     {
227       GNUNET_free (fn);
228       return GNUNET_SYSERR;
229     }
230   if ( (GNUNET_OK != GNUNET_BIO_read_int32__(fileR, "Read int32 error!", ranking)) ||
231        (GNUNET_OK != GNUNET_BIO_read_string(fileR, "Read string error!", ns_name, 200)) ||
232        (GNUNET_OK != GNUNET_BIO_read_meta_data(fileR, "Read meta data error!", meta)) )
233     {
234       GNUNET_BIO_read_close(fileR, &emsg);
235       GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
236       GNUNET_free (fn);
237       return GNUNET_SYSERR;
238     }
239   emsg = NULL;
240   if (GNUNET_OK != GNUNET_BIO_read_close(fileR, &emsg))
241     {
242       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
243                   _("Failed to parse metadata about pseudonym from file `%s': %s\n"),
244                   fn,
245                   emsg);
246       GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
247       GNUNET_free_non_null (emsg);
248       GNUNET_free (fn);
249       return GNUNET_SYSERR;
250     }
251   GNUNET_free (fn);
252   return GNUNET_OK;
253 }
254
255
256
257 /**
258  * Return the unique, human readable name for the given namespace.
259  *
260  * @param cfg configuration 
261  * @param nsid cryptographic ID of the namespace
262  * @return NULL on failure (should never happen)
263  */
264 char *
265 GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
266                              const GNUNET_HashCode * nsid)
267 {
268   struct GNUNET_CONTAINER_MetaData *meta;
269   char *name;
270   GNUNET_HashCode nh;
271   char *fn;
272   uint64_t len;
273   struct GNUNET_DISK_FileHandle *fh;
274   unsigned int i;
275   unsigned int idx;
276   char *ret;
277   struct stat sbuf;
278   int32_t temp = 0;
279   int32_t *rank = &temp;
280
281   meta = NULL;
282   name = NULL;
283   if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
284     {
285       if ((meta != NULL) && (name == NULL))
286         name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
287                                                               EXTRACTOR_TITLE,
288                                                               EXTRACTOR_FILENAME,
289                                                               EXTRACTOR_DESCRIPTION,
290                                                               EXTRACTOR_SUBJECT,
291                                                               EXTRACTOR_PUBLISHER,
292                                                               EXTRACTOR_AUTHOR,
293                                                               EXTRACTOR_COMMENT,
294                                                               EXTRACTOR_SUMMARY,
295                                                               EXTRACTOR_OWNER,
296                                                               -1);
297       if (meta != NULL)
298         {
299           GNUNET_CONTAINER_meta_data_destroy (meta);
300           meta = NULL;
301         }
302     }
303   if (name == NULL)
304     name = GNUNET_strdup (_("no-name"));
305   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
306   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
307   GNUNET_assert (fn != NULL);
308
309   len = 0;
310   if (0 == STAT (fn, &sbuf))
311     GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
312   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
313                               | GNUNET_DISK_OPEN_READWRITE,
314                               GNUNET_DISK_PERM_USER_READ |
315                               GNUNET_DISK_PERM_USER_WRITE);
316   i = 0;
317   idx = -1;
318   while ((len >= sizeof (GNUNET_HashCode)) &&
319          (sizeof (GNUNET_HashCode)
320           == GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
321     {
322       if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
323         {
324           idx = i;
325           break;
326         }
327       i++;
328       len -= sizeof (GNUNET_HashCode);
329     }
330   if (idx == -1)
331     {
332       idx = i;
333       if (sizeof (GNUNET_HashCode) !=
334           GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
335         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
336     }
337   GNUNET_DISK_file_close (fh);
338   ret = GNUNET_malloc (strlen (name) + 32);
339   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
340   GNUNET_free (name);
341   GNUNET_free (fn);
342   return ret;
343 }
344
345 /**
346  * Get the namespace ID belonging to the given namespace name.
347  *
348  * @param cfg configuration to use
349  * @param ns_uname human-readable name for the namespace
350  * @param nsid set to namespace ID based on 'ns_uname'
351  * @return GNUNET_OK on success
352  */
353 int
354 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
355                              const char *ns_uname, GNUNET_HashCode * nsid)
356 {
357   size_t slen;
358   uint64_t len;
359   unsigned int idx;
360   char *name;
361   GNUNET_HashCode nh;
362   char *fn;
363   struct GNUNET_DISK_FileHandle *fh;
364
365   idx = -1;
366   slen = strlen (ns_uname);
367   while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
368     slen--;
369   if (slen == 0)
370     return GNUNET_SYSERR;
371   name = GNUNET_strdup (ns_uname);
372   name[slen - 1] = '\0';
373   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
374   GNUNET_free (name);
375   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
376   GNUNET_assert (fn != NULL);
377
378   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
379        (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
380       ((idx + 1) * sizeof (GNUNET_HashCode) > len))
381     {
382       GNUNET_free (fn);
383       return GNUNET_SYSERR;
384     }
385   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
386                               | GNUNET_DISK_OPEN_READWRITE,
387                               GNUNET_DISK_PERM_USER_READ |
388                               GNUNET_DISK_PERM_USER_WRITE);
389   GNUNET_free (fn);
390   GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
391                          GNUNET_DISK_SEEK_SET);
392   if (sizeof (GNUNET_HashCode) !=
393       GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
394     {
395       GNUNET_DISK_file_close (fh);
396       return GNUNET_SYSERR;
397     }
398   GNUNET_DISK_file_close (fh);
399   return GNUNET_OK;
400 }
401
402
403
404 /**
405  * FIXME
406  */
407 struct ListPseudonymClosure
408 {
409
410   /**
411    * FIXME
412    */
413   GNUNET_PSEUDONYM_Iterator iterator;
414
415   /**
416    * FIXME
417    */
418   void *closure;
419
420   /**
421    * FIXME
422    */
423   const struct GNUNET_CONFIGURATION_Handle *cfg;
424 };
425
426
427
428 /**
429  * FIXME
430  */
431 static int
432 list_pseudonym_helper (void *cls, const char *fullname)
433 {
434   struct ListPseudonymClosure *c = cls;
435   int ret;
436   GNUNET_HashCode id;
437   int rating;
438   struct GNUNET_CONTAINER_MetaData *meta;
439   const char *fn;
440   char *str;
441
442   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
443     return GNUNET_OK;
444   fn =
445     &fullname[strlen (fullname) + 1 -
446               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
447   if (fn[-1] != DIR_SEPARATOR)
448     return GNUNET_OK;
449   ret = GNUNET_OK;
450   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
451     return GNUNET_OK;           /* invalid name */
452   str = NULL;
453   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
454     return GNUNET_OK;           /* ignore entry */
455   GNUNET_free_non_null (str);
456   if (c->iterator != NULL)
457     ret = c->iterator (c->closure, &id, meta, rating);
458   GNUNET_CONTAINER_meta_data_destroy (meta);
459   return ret;
460 }
461
462
463 /**
464  * List all available pseudonyms.
465  *
466  * @param cfg overall configuration 
467  * @param iterator function to call for each pseudonym
468  * @param closure closure for iterator
469  * @return number of pseudonyms found
470  */
471 int
472 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
473                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
474 {
475   struct ListPseudonymClosure cls;
476   char *fn;
477   int ret;
478
479   cls.iterator = iterator;
480   cls.closure = closure;
481   cls.cfg = cfg;
482   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
483   GNUNET_assert (fn != NULL);
484   GNUNET_DISK_directory_create (fn);
485   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
486   GNUNET_free (fn);
487   return ret;
488 }
489
490 /**
491  * Change the ranking of a pseudonym.
492  *
493  * @param cfg overall configuration
494  * @param nsid id of the pseudonym
495  * @param delta by how much should the rating be
496  *  changed?
497  * @return new rating of the pseudonym
498  */
499 int
500 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
501                        const GNUNET_HashCode * nsid, int delta)
502 {
503   struct GNUNET_CONTAINER_MetaData *meta;
504   int ret;
505   int32_t ranking;
506   char *name;
507
508   name = NULL;
509   ret = read_info (cfg, nsid, &meta, &ranking, &name);
510   if (ret == GNUNET_SYSERR)
511     {
512       ranking = 0;
513       meta = GNUNET_CONTAINER_meta_data_create ();
514     }
515   ranking += delta;
516   write_pseudonym_info (cfg, nsid, meta, ranking, name);
517   GNUNET_CONTAINER_meta_data_destroy (meta);
518   GNUNET_free_non_null (name);
519   return ranking;
520 }
521
522 /**
523  * Insert metadata into existing MD record (passed as cls).
524  *
525  * @param cls metadata to add to
526  * @param type type of entry to insert
527  * @param data value of entry to insert
528  */
529 static int
530 merge_meta_helper (void *cls, EXTRACTOR_KeywordType type, const char *data)
531 {
532   struct GNUNET_CONTAINER_MetaData *meta = cls;
533   GNUNET_CONTAINER_meta_data_insert (meta, type, data);
534   return GNUNET_OK;
535 }
536
537
538
539 /**
540  * Add a pseudonym to the set of known pseudonyms.
541  * For all pseudonym advertisements that we discover
542  * FS should automatically call this function.
543  *
544  * @param cfg overall configuration
545  * @param id the pseudonym identifier
546  * @param meta metadata for the pseudonym
547  */
548 void
549 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
550                       const GNUNET_HashCode * id,
551                       const struct GNUNET_CONTAINER_MetaData *meta)
552 {
553   char *name;
554   int32_t ranking;
555   struct GNUNET_CONTAINER_MetaData *old;
556   char *fn;
557   struct stat sbuf;
558
559   ranking = 0;
560   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
561   GNUNET_assert (fn != NULL);
562
563   if ((0 == STAT (fn, &sbuf)) &&
564       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
565     {
566       GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
567       write_pseudonym_info (cfg, id, old, ranking, name);
568       GNUNET_CONTAINER_meta_data_destroy (old);
569       GNUNET_free_non_null (name);
570     }
571   else
572     {
573       write_pseudonym_info (cfg, id, meta, ranking, NULL);
574     }
575   GNUNET_free (fn);
576   internal_notify (id, meta, ranking);
577 }
578
579
580
581
582
583 /* end of pseudonym.c */