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