-hacking get, rename, delete on client side
[oweals/gnunet.git] / src / identity / identity_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 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 Liceidentity as published
7      by the Free Software Foundation; either version 3, 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 Liceidentity for more details.
14
15      You should have received a copy of the GNU General Public Liceidentity
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 identity/identity_api.c
23  * @brief api to interact with the identity service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_client_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_server_lib.h"
34 #include "gnunet_time_lib.h"
35 #include "gnunet_identity_service.h"
36 #include "identity.h"
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "identity-api",__VA_ARGS__)
39
40 /** 
41  * Handle for a ego.
42  */
43 struct GNUNET_IDENTITY_Ego
44 {
45   /**
46    * Private key associated with this ego.
47    */
48   struct GNUNET_CRYPTO_EccPrivateKey *pk;
49
50   /**
51    * Current identifier (name) associated with this ego.
52    */
53   char *identifier;
54
55   /**
56    * Client context associated with this ego.
57    */
58   void *ctx;
59
60   /**
61    * Hash of the public key of this ego.
62    */
63   struct GNUNET_HashCode id;
64 };
65
66
67 /** 
68  * Handle for an operation with the identity service.
69  */
70 struct GNUNET_IDENTITY_Operation
71 {
72
73   /**
74    * Main identity handle.
75    */
76   struct GNUNET_IDENTITY_Handle *h;
77   
78   /**
79    * We keep operations in a DLL.
80    */
81   struct GNUNET_IDENTITY_Operation *next;
82
83   /**
84    * We keep operations in a DLL.
85    */
86   struct GNUNET_IDENTITY_Operation *prev;
87
88   /**
89    * Message to send to the identity service.
90    * Allocated at the end of this struct.
91    */
92   const struct GNUNET_MessageHeader *msg;
93
94   /**
95    * Continuation to invoke with the result of the transmission; 'cb'
96    * will be NULL in this case.
97    */
98   GNUNET_IDENTITY_Continuation cont;
99
100   /**
101    * Continuation to invoke with the result of the transmission for
102    * 'get' operations ('cont' will be NULL in this case).
103    */
104   GNUNET_IDENTITY_Callback cb;
105
106   /**
107    * Closure for 'cont' or 'cb'.
108    */
109   void *cls;
110
111 };
112
113
114 /**
115  * Handle for the service.
116  */
117 struct GNUNET_IDENTITY_Handle
118 {
119   /**
120    * Configuration to use.
121    */
122   const struct GNUNET_CONFIGURATION_Handle *cfg;
123
124   /**
125    * Socket (if available).
126    */
127   struct GNUNET_CLIENT_Connection *client;
128
129   /**
130    * Hash map from the hash of the public key to the
131    * respective 'GNUNET_IDENTITY_Ego' handle.
132    */
133   struct GNUNET_CONTAINER_MultiHashMap *egos;
134
135   /**
136    * Function to call when we receive updates.
137    */
138   GNUNET_IDENTITY_Callback cb;
139
140   /**
141    * Closure for 'cb'.
142    */
143   void *cb_cls;
144
145   /**
146    * Head of active operations.
147    */ 
148   struct GNUNET_IDENTITY_Operation *op_head;
149
150   /**
151    * Tail of active operations.
152    */ 
153   struct GNUNET_IDENTITY_Operation *op_tail;
154
155   /**
156    * Currently pending transmission request, or NULL for none.
157    */
158   struct GNUNET_CLIENT_TransmitHandle *th;
159
160   /**
161    * Task doing exponential back-off trying to reconnect.
162    */
163   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
164
165   /**
166    * Time for next connect retry.
167    */
168   struct GNUNET_TIME_Relative reconnect_delay;
169
170   /**
171    * Are we polling for incoming messages right now?
172    */
173   int in_receive;
174
175 };
176
177
178
179 /**
180  * Try again to connect to network size estimation service.
181  *
182  * @param cls the handle to the transport service
183  * @param tc scheduler context
184  */
185 static void
186 reconnect (void *cls,
187            const struct GNUNET_SCHEDULER_TaskContext *tc);
188
189
190 /**
191  * Reschedule a connect attempt to the service.
192  *
193  * @param h transport service to reconnect
194  */
195 static void
196 reschedule_connect (struct GNUNET_IDENTITY_Handle *h)
197 {
198   GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
199
200   if (NULL != h->th)
201   {
202     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
203     h->th = NULL;
204   }
205   if (NULL != h->client)
206   {
207     GNUNET_CLIENT_disconnect (h->client);
208     h->client = NULL;
209   }
210   h->in_receive = GNUNET_NO;
211   LOG (GNUNET_ERROR_TYPE_DEBUG,
212        "Scheduling task to reconnect to identity service in %s.\n",
213        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
214   h->reconnect_task =
215       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
216   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
217 }
218
219
220 /**
221  * Type of a function to call when we receive a message
222  * from the service.
223  *
224  * @param cls closure
225  * @param msg message received, NULL on timeout or fatal error
226  */
227 static void
228 message_handler (void *cls, 
229                  const struct GNUNET_MessageHeader *msg)
230 {
231   struct GNUNET_IDENTITY_Handle *h = cls;
232   struct GNUNET_IDENTITY_Operation *op;
233   struct GNUNET_IDENTITY_Ego *ego;
234   const struct GNUNET_IDENTITY_ResultCodeMessage *rcm;
235   const struct GNUNET_IDENTITY_UpdateMessage *um;
236   const struct GNUNET_IDENTITY_SetDefaultMessage *sdm;
237   struct GNUNET_CRYPTO_EccPrivateKey *priv;
238   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub;
239   struct GNUNET_HashCode id;
240   const char *str;
241   uint16_t size;
242   uint16_t pk_len;
243   uint16_t name_len;
244
245   if (NULL == msg)
246   {
247     reschedule_connect (h);
248     return;
249   }
250   size = ntohs (msg->size);
251   switch (ntohs (msg->type))
252   {
253   case GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE:
254     if (size < sizeof (struct GNUNET_IDENTITY_ResultCodeMessage))
255     {
256       GNUNET_break (0);
257       reschedule_connect (h);
258       return;
259     }
260     rcm = (const struct GNUNET_IDENTITY_ResultCodeMessage *) msg;
261     str = (const char *) &rcm[1];
262     if ( (size > sizeof (struct GNUNET_IDENTITY_ResultCodeMessage)) &&
263          ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_ResultCodeMessage) - 1]) )
264     {
265       GNUNET_break (0);
266       reschedule_connect (h);
267       return;
268     }
269     if (size == sizeof (struct GNUNET_IDENTITY_ResultCodeMessage))
270       str = NULL;
271
272     op = h->op_head;
273     GNUNET_CONTAINER_DLL_remove (h->op_head,
274                                  h->op_tail,
275                                  op);
276     if (NULL != op->cont)
277       op->cont (op->cls,
278                 str);
279     GNUNET_break (NULL == op->cb);
280     GNUNET_free (op);
281     break;
282   case GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE:
283     if (size < sizeof (struct GNUNET_IDENTITY_UpdateMessage))
284     {
285       GNUNET_break (0);
286       reschedule_connect (h);
287       return;
288     }
289     um = (const struct GNUNET_IDENTITY_UpdateMessage *) msg;
290     pk_len = ntohs (um->pk_len);
291     name_len = ntohs (um->name_len);    
292     str = (const char *) &um[1];
293     if ( (size != pk_len + name_len + sizeof (struct GNUNET_IDENTITY_UpdateMessage)) ||
294          ( (0 != name_len) &&
295            ('\0' != str[pk_len + name_len - 1])) )
296     {
297       GNUNET_break (0);
298       reschedule_connect (h);
299       return;
300     }
301     priv = GNUNET_CRYPTO_ecc_decode_key (str, pk_len, GNUNET_YES); 
302     if (NULL == priv)
303     {
304       GNUNET_break (0);
305       reschedule_connect (h);
306       return;
307     }
308     GNUNET_CRYPTO_ecc_key_get_public (priv,
309                                       &pub);
310     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &id);
311     if (0 == name_len)
312       str = NULL;
313     else
314       str = &str[pk_len];
315     ego = GNUNET_CONTAINER_multihashmap_get (h->egos,
316                                              &id);
317     if (NULL == ego)
318     {
319       /* ego was created */
320       if (NULL == str)
321       {
322         /* deletion of unknown ego? not allowed */
323         GNUNET_break (0);
324         GNUNET_CRYPTO_ecc_key_free (priv);
325         reschedule_connect (h);
326         return;
327       }
328       ego = GNUNET_new (struct GNUNET_IDENTITY_Ego);
329       ego->pk = priv;
330       ego->identifier = GNUNET_strdup (str);
331       ego->id = id;
332       GNUNET_assert (GNUNET_YES ==
333                      GNUNET_CONTAINER_multihashmap_put (h->egos,
334                                                         &ego->id,
335                                                         ego,
336                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
337     }
338     else
339     {
340       GNUNET_CRYPTO_ecc_key_free (priv);
341     }
342     /* inform application about change */
343     h->cb (h->cb_cls,
344            ego,
345            &ego->ctx,
346            str);
347     if (NULL == str)
348     {
349       /* ego was deleted */
350       GNUNET_assert (GNUNET_YES ==
351                      GNUNET_CONTAINER_multihashmap_remove (h->egos,
352                                                            &ego->id,
353                                                            ego));
354       GNUNET_CRYPTO_ecc_key_free (ego->pk);
355       GNUNET_free (ego->identifier);
356       GNUNET_free (ego);
357     }
358     else
359       {
360       /* ego changed name */
361       GNUNET_free (ego->identifier);
362       ego->identifier = GNUNET_strdup (str);
363     }    
364     break;
365   case GNUNET_MESSAGE_TYPE_IDENTITY_SET_DEFAULT:
366     if (size < sizeof (struct GNUNET_IDENTITY_SetDefaultMessage))
367     {
368       GNUNET_break (0);
369       reschedule_connect (h);
370       return;
371     }
372     sdm = (const struct GNUNET_IDENTITY_SetDefaultMessage *) msg;
373     pk_len = ntohs (sdm->pk_len);
374     name_len = ntohs (sdm->name_len);
375     str = (const char *) &sdm[1];
376     if ( (size != pk_len + name_len + sizeof (struct GNUNET_IDENTITY_SetDefaultMessage)) ||
377          ( (0 != name_len) &&
378            ('\0' != str[pk_len + name_len - 1]) ) )
379     {
380       GNUNET_break (0);
381       reschedule_connect (h);
382       return;
383     }
384     priv = GNUNET_CRYPTO_ecc_decode_key (str, pk_len, GNUNET_YES); 
385     if (NULL == priv)
386     {
387       GNUNET_break (0);
388       reschedule_connect (h);
389       return;
390     }
391     GNUNET_CRYPTO_ecc_key_get_public (priv,
392                                       &pub);
393     GNUNET_CRYPTO_ecc_key_free (priv);
394     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &id);
395     if (0 == name_len)
396       str = NULL;
397     else
398       str = &str[pk_len];
399     ego = GNUNET_CONTAINER_multihashmap_get (h->egos,
400                                              &id);
401     if (NULL == ego)
402     {
403       GNUNET_break (0);
404       reschedule_connect (h);
405       return;
406     }
407     op = h->op_head;
408     GNUNET_CONTAINER_DLL_remove (h->op_head,
409                                  h->op_tail,
410                                  op);
411     if (NULL != op->cb)
412       op->cb (op->cls,
413               ego,
414               &ego->ctx,
415               ego->identifier);
416     GNUNET_break (NULL == op->cont);
417     GNUNET_free (op);
418     break;
419   default:
420     GNUNET_break (0);
421     reschedule_connect (h);
422     return;
423   }
424   GNUNET_CLIENT_receive (h->client, &message_handler, h,
425                          GNUNET_TIME_UNIT_FOREVER_REL);
426 }
427
428
429 /**
430  * Schedule transmission of the next message from our queue.
431  *
432  * @param h identity handle
433  */
434 static void
435 transmit_next (struct GNUNET_IDENTITY_Handle *h);
436
437
438 /**
439  * Transmit next message to service.
440  *
441  * @param cls the 'struct GNUNET_IDENTITY_Handle'.
442  * @param size number of bytes available in buf
443  * @param buf where to copy the message
444  * @return number of bytes copied to buf
445  */
446 static size_t
447 send_next_message (void *cls, 
448                    size_t size, 
449                    void *buf)
450 {
451   struct GNUNET_IDENTITY_Handle *h = cls;
452   struct GNUNET_IDENTITY_Operation *op = h->op_head;
453   size_t ret;
454   
455   h->th = NULL;
456   if (NULL == op)
457     return 0;
458   ret = ntohs (op->msg->size);
459   if (ret > size)
460   {
461     reschedule_connect (h);
462     return 0;
463   }  
464   memcpy (buf, op->msg, ret);
465   if ( (NULL == op->cont) &&
466        (NULL == op->cb) )
467   {
468     GNUNET_CONTAINER_DLL_remove (h->op_head,
469                                  h->op_tail,
470                                  op);
471     GNUNET_free (op);
472     transmit_next (h);
473   }
474   if (GNUNET_NO == h->in_receive)
475   {
476     h->in_receive = GNUNET_YES;
477     GNUNET_CLIENT_receive (h->client,
478                            &message_handler, h,
479                            GNUNET_TIME_UNIT_FOREVER_REL);
480   }
481   return ret;
482 }
483
484
485 /**
486  * Schedule transmission of the next message from our queue.
487  *
488  * @param h identity handle
489  */
490 static void
491 transmit_next (struct GNUNET_IDENTITY_Handle *h)
492 {
493   struct GNUNET_IDENTITY_Operation *op = h->op_head;
494
495   GNUNET_assert (NULL == h->th);
496   if (NULL == op)
497     return;
498   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
499                                                ntohs (op->msg->size),
500                                                GNUNET_TIME_UNIT_FOREVER_REL,
501                                                GNUNET_NO,
502                                                &send_next_message,
503                                                h);
504 }
505
506
507 /**
508  * Try again to connect to network size estimation service.
509  *
510  * @param cls the handle to the transport service
511  * @param tc scheduler context
512  */
513 static void
514 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
515 {
516   struct GNUNET_IDENTITY_Handle *h = cls;
517   struct GNUNET_IDENTITY_Operation *op;
518   struct GNUNET_MessageHeader msg;
519
520   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
521   LOG (GNUNET_ERROR_TYPE_DEBUG,
522        "Connecting to identity service.\n");
523   GNUNET_assert (NULL == h->client);
524   h->client = GNUNET_CLIENT_connect ("identity", h->cfg);
525   GNUNET_assert (NULL != h->client);
526   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + 
527                       sizeof (struct GNUNET_MessageHeader));
528   op->h = h;
529   op->msg = (const struct GNUNET_MessageHeader *) &op[1];
530   msg.size = htons (sizeof (msg));
531   msg.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_START);
532   memcpy (&op[1], &msg, sizeof (msg));
533   GNUNET_CONTAINER_DLL_insert (h->op_head,
534                                h->op_tail,
535                                op);
536   transmit_next (h);
537   GNUNET_assert (NULL != h->th);
538 }
539
540
541 /**
542  * Connect to the identity service.
543  *
544  * @param cfg the configuration to use
545  * @param cb function to call on all identity events, can be NULL
546  * @param cb_cls closure for 'cb'
547  * @return handle to use
548  */
549 struct GNUNET_IDENTITY_Handle *
550 GNUNET_IDENTITY_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
551                          GNUNET_IDENTITY_Callback cb,
552                          void *cb_cls)
553 {
554   struct GNUNET_IDENTITY_Handle *h;
555
556   h = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Handle));
557   h->cfg = cfg;
558   h->cb = cb;
559   h->cb_cls = cb_cls;
560   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
561   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
562   return h;
563 }
564
565
566 /**
567  * Obtain the ECC key associated with a ego.
568  *
569  * @param ego the ego
570  * @return associated ECC key, valid as long as the ego is valid
571  */
572 const struct GNUNET_CRYPTO_EccPrivateKey *
573 GNUNET_IDENTITY_ego_get_key (struct GNUNET_IDENTITY_Ego *ego)
574 {
575   return ego->pk;
576 }
577
578
579 /**
580  * Obtain the identity that is currently preferred/default
581  * for a service.
582  *
583  * @param id identity service to query
584  * @param service_name for which service is an identity wanted
585  * @param cb function to call with the result (will only be called once)
586  * @param cb_cls closure for cb
587  * @return handle to abort the operation
588  */
589 struct GNUNET_IDENTITY_Operation *
590 GNUNET_IDENTITY_get (struct GNUNET_IDENTITY_Handle *id,
591                      const char *service_name,
592                      GNUNET_IDENTITY_Callback cb,
593                      void *cb_cls)
594 {
595   struct GNUNET_IDENTITY_Operation *op;
596   struct GNUNET_IDENTITY_GetDefaultMessage *gdm;
597   size_t slen;
598
599   slen = strlen (service_name) + 1; 
600   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_GetDefaultMessage))
601   {
602     GNUNET_break (0);
603     return NULL;
604   }
605   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) +
606                       sizeof (struct GNUNET_IDENTITY_GetDefaultMessage) +
607                       slen);  
608   op->cb = cb;
609   op->cls = cb_cls;
610   gdm = (struct GNUNET_IDENTITY_GetDefaultMessage *) &op[1];
611   gdm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_GET_DEFAULT);
612   gdm->header.size = htons (sizeof (struct GNUNET_IDENTITY_GetDefaultMessage) +
613                             slen);
614   gdm->name_len = htons (slen);
615   gdm->reserved = htons (0);
616   memcpy (&gdm[1], service_name, slen);
617   op->msg = &gdm->header;
618   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
619                                     id->op_tail,
620                                     op);
621   if (NULL == id->th)
622     transmit_next (id);
623   return op;
624 }
625
626
627 /**
628  * Set the preferred/default identity for a service.
629  *
630  * @param id identity service to inform
631  * @param service_name for which service is an identity set
632  * @param ego new default identity to be set for this service
633  * @param cont function to call once the operation finished
634  * @param cont_cls closure for cont
635  * @return handle to abort the operation
636  */
637 struct GNUNET_IDENTITY_Operation *
638 GNUNET_IDENTITY_set (struct GNUNET_IDENTITY_Handle *id,
639                      const char *service_name,
640                      struct GNUNET_IDENTITY_Ego *ego,
641                      GNUNET_IDENTITY_Continuation cont,
642                      void *cont_cls)
643 {
644   GNUNET_break (0); // FIXME
645   return NULL;
646 }
647
648
649 /** 
650  * Create a new identity with the given identifier.
651  *
652  * @param id identity service to use
653  * @param identifier desired identifier
654  * @param cb function to call with the result (will only be called once)
655  * @param cb_cls closure for cb
656  * @return handle to abort the operation
657  */
658 struct GNUNET_IDENTITY_Operation *
659 GNUNET_IDENTITY_create (struct GNUNET_IDENTITY_Handle *id,
660                         const char *identifier,
661                         GNUNET_IDENTITY_Callback cb,
662                         void *cb_cls)
663 {
664   GNUNET_break (0); // FIXME
665   return NULL;
666 }
667
668
669 /** 
670  * Renames an existing identity.
671  *
672  * @param id identity service to use
673  * @param old_identifier old identifier
674  * @param new_identifier desired new identifier
675  * @param cb function to call with the result (will only be called once)
676  * @param cb_cls closure for cb
677  * @return handle to abort the operation
678  */
679 struct GNUNET_IDENTITY_Operation *
680 GNUNET_IDENTITY_rename (struct GNUNET_IDENTITY_Handle *id,
681                         const char *old_identifier,
682                         const char *new_identifier,
683                         GNUNET_IDENTITY_Continuation cb,
684                         void *cb_cls)
685 {
686   struct GNUNET_IDENTITY_Operation *op;
687   struct GNUNET_IDENTITY_RenameMessage *grm;
688   size_t slen_old;
689   size_t slen_new;
690   char *dst;
691
692   slen_old = strlen (old_identifier) + 1;
693   slen_new = strlen (new_identifier) + 1;
694   if ( (slen_old >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
695        (slen_new >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
696        (slen_old + slen_new >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_RenameMessage)) )
697   {
698     GNUNET_break (0);
699     return NULL;
700   }
701   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) +
702                       sizeof (struct GNUNET_IDENTITY_RenameMessage) +
703                       slen_old + slen_new);
704   op->cont = cb;
705   op->cls = cb_cls;
706   grm = (struct GNUNET_IDENTITY_RenameMessage *) &op[1];
707   grm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_RENAME);
708   grm->header.size = htons (sizeof (struct GNUNET_IDENTITY_RenameMessage) +
709                             slen_old + slen_new);
710   grm->old_name_len = htons (slen_old);
711   grm->new_name_len = htons (slen_new);
712   dst = (char *) &grm[1];
713   memcpy (dst, old_identifier, slen_old);
714   memcpy (&dst[slen_old], new_identifier, slen_new);
715   op->msg = &grm->header;
716   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
717                                     id->op_tail,
718                                     op);
719   if (NULL == id->th)
720     transmit_next (id);
721   return op;
722 }
723
724
725 /** 
726  * Delete an existing identity.
727  *
728  * @param id identity service to use
729  * @param identifier identifier of the identity to delete
730  * @param cb function to call with the result (will only be called once)
731  * @param cb_cls closure for cb
732  * @return handle to abort the operation
733  */
734 struct GNUNET_IDENTITY_Operation *
735 GNUNET_IDENTITY_delete (struct GNUNET_IDENTITY_Handle *id,
736                         const char *identifier,
737                         GNUNET_IDENTITY_Continuation cb,
738                         void *cb_cls)
739 {
740   struct GNUNET_IDENTITY_Operation *op;
741   struct GNUNET_IDENTITY_DeleteMessage *gdm;
742   size_t slen;
743
744   slen = strlen (identifier) + 1;
745   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_DeleteMessage))
746   {
747     GNUNET_break (0);
748     return NULL;
749   }
750   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) +
751                       sizeof (struct GNUNET_IDENTITY_DeleteMessage) +
752                       slen);  
753   op->cont = cb;
754   op->cls = cb_cls;
755   gdm = (struct GNUNET_IDENTITY_DeleteMessage *) &op[1];
756   gdm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_DELETE);
757   gdm->header.size = htons (sizeof (struct GNUNET_IDENTITY_DeleteMessage) +
758                             slen);
759   gdm->name_len = htons (slen);
760   gdm->reserved = htons (0);
761   memcpy (&gdm[1], identifier, slen);
762   op->msg = &gdm->header;
763   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
764                                     id->op_tail,
765                                     op);
766   if (NULL == id->th)
767     transmit_next (id);
768   return op;
769 }
770
771
772 /**
773  * Cancel an identity operation. Note that the operation MAY still
774  * be executed; this merely cancels the continuation; if the request
775  * was already transmitted, the service may still choose to complete
776  * the operation.
777  *
778  * @param op operation to cancel
779  */
780 void
781 GNUNET_IDENITY_cancel (struct GNUNET_IDENTITY_Operation *op)
782 {
783   struct GNUNET_IDENTITY_Handle *h = op->h;
784
785   if ( (h->op_head != op) ||
786        (NULL == h->client) )
787   {
788     /* request not active, can simply remove */
789     GNUNET_CONTAINER_DLL_remove (h->op_head,
790                                  h->op_tail,
791                                  op);
792     GNUNET_free (op);
793     return;
794   }
795   if (NULL != h->th)
796   {
797     /* request active but not yet with service, can still abort */
798     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
799     h->th = NULL;
800     GNUNET_CONTAINER_DLL_remove (h->op_head,
801                                  h->op_tail,
802                                  op);
803     GNUNET_free (op);
804     transmit_next (h);
805     return;
806   }
807   /* request active with service, simply ensure continuations are not called */
808   op->cont = NULL;
809   op->cb = NULL;
810 }
811
812
813 /**
814  * Disconnect from identity service
815  *
816  * @param h handle to destroy
817  */
818 void
819 GNUNET_IDENTITY_disconnect (struct GNUNET_IDENTITY_Handle *h)
820 {
821   GNUNET_assert (NULL != h);
822   if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
823   {
824     GNUNET_SCHEDULER_cancel (h->reconnect_task);
825     h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
826   }
827   if (NULL != h->th)
828   {
829     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
830     h->th = NULL;
831   }
832   if (NULL != h->client)
833   {
834     GNUNET_CLIENT_disconnect (h->client);
835     h->client = NULL;
836   }
837   GNUNET_free (h);
838 }
839
840 /* end of identity_api.c */