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