bc7229c727b566ed5335a81e49910512a6a81ba0
[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
181   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
182   GNUNET_assert (fn != NULL);
183   struct GNUNET_BIO_WriteHandle *fileW;
184   fileW = GNUNET_BIO_write_open(fn);
185   GNUNET_assert (NULL != fileW);
186   GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32(fileW, ranking));
187   GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_string(fileW, ns_name));
188   GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_meta_data(fileW, meta));
189   GNUNET_assert(GNUNET_OK == GNUNET_BIO_write_close(fileW));
190   GNUNET_free (fn);
191   /* create entry for pseudonym name in names */
192   GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
193 }
194
195
196 /**
197  * FIXME
198  */
199 static int
200 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
201            const GNUNET_HashCode * nsid,
202            struct GNUNET_CONTAINER_MetaData **meta,
203            int32_t * ranking, char **ns_name)
204 {
205   char *fn;
206   char *emsg;
207
208   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
209   GNUNET_assert (fn != NULL);
210   struct GNUNET_BIO_ReadHandle *fileR;
211   fileR = GNUNET_BIO_read_open(fn);
212   GNUNET_assert (NULL != fileR);
213   GNUNET_assert (GNUNET_OK == GNUNET_BIO_read_int32__(fileR, "Read int32 error!", ranking));
214   GNUNET_assert (GNUNET_OK == GNUNET_BIO_read_string(fileR, "Read string error!", ns_name, 200));
215   GNUNET_assert (GNUNET_OK == GNUNET_BIO_read_meta_data(fileR, "Read meta data error!", meta));
216   GNUNET_assert(GNUNET_OK == GNUNET_BIO_read_close(fileR, &emsg));
217   GNUNET_free (fn);
218   return GNUNET_OK;
219 }
220
221
222
223 /**
224  * Return the unique, human readable name for the given namespace.
225  *
226  * @param cfg configuration 
227  * @param nsid cryptographic ID of the namespace
228  * @return NULL on failure (should never happen)
229  */
230 char *
231 GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
232                              const GNUNET_HashCode * nsid)
233 {
234   struct GNUNET_CONTAINER_MetaData *meta;
235   char *name;
236   GNUNET_HashCode nh;
237   char *fn;
238   uint64_t len;
239   struct GNUNET_DISK_FileHandle *fh;
240   unsigned int i;
241   unsigned int idx;
242   char *ret;
243   struct stat sbuf;
244   int32_t temp = 0;
245   int32_t *rank = &temp;
246
247   meta = NULL;
248   name = NULL;
249   if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
250     {
251       if ((meta != NULL) && (name == NULL))
252         name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
253                                                               EXTRACTOR_TITLE,
254                                                               EXTRACTOR_FILENAME,
255                                                               EXTRACTOR_DESCRIPTION,
256                                                               EXTRACTOR_SUBJECT,
257                                                               EXTRACTOR_PUBLISHER,
258                                                               EXTRACTOR_AUTHOR,
259                                                               EXTRACTOR_COMMENT,
260                                                               EXTRACTOR_SUMMARY,
261                                                               EXTRACTOR_OWNER,
262                                                               -1);
263       if (meta != NULL)
264         {
265           GNUNET_CONTAINER_meta_data_destroy (meta);
266           meta = NULL;
267         }
268     }
269   if (name == NULL)
270     name = GNUNET_strdup (_("no-name"));
271   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
272   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
273   GNUNET_assert (fn != NULL);
274
275   len = 0;
276   if (0 == STAT (fn, &sbuf))
277     GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
278   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
279                               | GNUNET_DISK_OPEN_READWRITE,
280                               GNUNET_DISK_PERM_USER_READ |
281                               GNUNET_DISK_PERM_USER_WRITE);
282   i = 0;
283   idx = -1;
284   while ((len >= sizeof (GNUNET_HashCode)) &&
285          (sizeof (GNUNET_HashCode)
286           == GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
287     {
288       if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
289         {
290           idx = i;
291           break;
292         }
293       i++;
294       len -= sizeof (GNUNET_HashCode);
295     }
296   if (idx == -1)
297     {
298       idx = i;
299       if (sizeof (GNUNET_HashCode) !=
300           GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
301         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
302     }
303   GNUNET_DISK_file_close (fh);
304   ret = GNUNET_malloc (strlen (name) + 32);
305   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
306   GNUNET_free (name);
307   GNUNET_free (fn);
308   return ret;
309 }
310
311 /**
312  * Get the namespace ID belonging to the given namespace name.
313  *
314  * @param cfg configuration to use
315  * @param ns_uname human-readable name for the namespace
316  * @param nsid set to namespace ID based on 'ns_uname'
317  * @return GNUNET_OK on success
318  */
319 int
320 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
321                              const char *ns_uname, GNUNET_HashCode * nsid)
322 {
323   size_t slen;
324   uint64_t len;
325   unsigned int idx;
326   char *name;
327   GNUNET_HashCode nh;
328   char *fn;
329   struct GNUNET_DISK_FileHandle *fh;
330
331   idx = -1;
332   slen = strlen (ns_uname);
333   while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
334     slen--;
335   if (slen == 0)
336     return GNUNET_SYSERR;
337   name = GNUNET_strdup (ns_uname);
338   name[slen - 1] = '\0';
339   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
340   GNUNET_free (name);
341   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
342   GNUNET_assert (fn != NULL);
343
344   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
345        (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
346       ((idx + 1) * sizeof (GNUNET_HashCode) > len))
347     {
348       GNUNET_free (fn);
349       return GNUNET_SYSERR;
350     }
351   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
352                               | GNUNET_DISK_OPEN_READWRITE,
353                               GNUNET_DISK_PERM_USER_READ |
354                               GNUNET_DISK_PERM_USER_WRITE);
355   GNUNET_free (fn);
356   GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
357                          GNUNET_DISK_SEEK_SET);
358   if (sizeof (GNUNET_HashCode) !=
359       GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
360     {
361       GNUNET_DISK_file_close (fh);
362       return GNUNET_SYSERR;
363     }
364   GNUNET_DISK_file_close (fh);
365   return GNUNET_OK;
366 }
367
368
369
370 /**
371  * FIXME
372  */
373 struct ListPseudonymClosure
374 {
375
376   /**
377    * FIXME
378    */
379   GNUNET_PSEUDONYM_Iterator iterator;
380
381   /**
382    * FIXME
383    */
384   void *closure;
385
386   /**
387    * FIXME
388    */
389   const struct GNUNET_CONFIGURATION_Handle *cfg;
390 };
391
392
393
394 /**
395  * FIXME
396  */
397 static int
398 list_pseudonym_helper (void *cls, const char *fullname)
399 {
400   struct ListPseudonymClosure *c = cls;
401   int ret;
402   GNUNET_HashCode id;
403   int rating;
404   struct GNUNET_CONTAINER_MetaData *meta;
405   const char *fn;
406   const char *str = "not null";
407
408   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
409     return GNUNET_OK;
410   fn =
411     &fullname[strlen (fullname) + 1 -
412               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
413   if (fn[-1] != DIR_SEPARATOR)
414     return GNUNET_OK;
415   ret = GNUNET_OK;
416   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
417     return GNUNET_OK;           /* invalid name */
418   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
419     return GNUNET_OK;           /* ignore entry */
420   if (c->iterator != NULL)
421     ret = c->iterator (c->closure, &id, meta, rating);
422   GNUNET_CONTAINER_meta_data_destroy (meta);
423   return ret;
424 }
425
426
427 /**
428  * List all available pseudonyms.
429  *
430  * @param cfg overall configuration 
431  * @param iterator function to call for each pseudonym
432  * @param closure closure for iterator
433  * @return number of pseudonyms found
434  */
435 int
436 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
437                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
438 {
439   struct ListPseudonymClosure cls;
440   char *fn;
441   int ret;
442
443   cls.iterator = iterator;
444   cls.closure = closure;
445   cls.cfg = cfg;
446   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
447   GNUNET_assert (fn != NULL);
448   GNUNET_DISK_directory_create (fn);
449   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
450   GNUNET_free (fn);
451   return ret;
452 }
453
454 /**
455  * Change the ranking of a pseudonym.
456  *
457  * @param cfg overall configuration
458  * @param nsid id of the pseudonym
459  * @param delta by how much should the rating be
460  *  changed?
461  * @return new rating of the pseudonym
462  */
463 int
464 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
465                        const GNUNET_HashCode * nsid, int delta)
466 {
467   struct GNUNET_CONTAINER_MetaData *meta;
468   int ret;
469   int32_t ranking;
470   char *name;
471
472   name = NULL;
473   ret = read_info (cfg, nsid, &meta, &ranking, &name);
474   if (ret == GNUNET_SYSERR)
475     {
476       ranking = 0;
477       meta = GNUNET_CONTAINER_meta_data_create ();
478     }
479   ranking += delta;
480   write_pseudonym_info (cfg, nsid, meta, ranking, name);
481   GNUNET_CONTAINER_meta_data_destroy (meta);
482   GNUNET_free_non_null (name);
483   return ranking;
484 }
485
486 /**
487  * Insert metadata into existing MD record (passed as cls).
488  *
489  * @param cls metadata to add to
490  * @param type type of entry to insert
491  * @param data value of entry to insert
492  */
493 static int
494 merge_meta_helper (void *cls, EXTRACTOR_KeywordType type, const char *data)
495 {
496   struct GNUNET_CONTAINER_MetaData *meta = cls;
497   GNUNET_CONTAINER_meta_data_insert (meta, type, data);
498   return GNUNET_OK;
499 }
500
501
502
503 /**
504  * Add a pseudonym to the set of known pseudonyms.
505  * For all pseudonym advertisements that we discover
506  * FS should automatically call this function.
507  *
508  * @param cfg overall configuration
509  * @param id the pseudonym identifier
510  * @param meta metadata for the pseudonym
511  */
512 void
513 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
514                       const GNUNET_HashCode * id,
515                       const struct GNUNET_CONTAINER_MetaData *meta)
516 {
517   char *name;
518   int32_t ranking;
519   struct GNUNET_CONTAINER_MetaData *old;
520   char *fn;
521   struct stat sbuf;
522
523   ranking = 0;
524   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
525   GNUNET_assert (fn != NULL);
526
527   if ((0 == STAT (fn, &sbuf)) &&
528       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
529     {
530       GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
531       write_pseudonym_info (cfg, id, old, ranking, name);
532       GNUNET_CONTAINER_meta_data_destroy (old);
533       GNUNET_free_non_null (name);
534     }
535   else
536     {
537       write_pseudonym_info (cfg, id, meta, ranking, NULL);
538     }
539   GNUNET_free (fn);
540   internal_notify (id, meta, ranking);
541 }
542
543
544
545
546
547 /* end of pseudonym.c */