handling of unindex
[oweals/gnunet.git] / src / fs / gnunet-service-fs.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 fs/gnunet-service-fs.c
23  * @brief program that provides the file-sharing service
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - read_index_list
28  * - write_index_list
29  * - bloomfilter support (GET, CS-request with BF, etc.)
30  * - handling of on-demand blocks from datastore (need to use indexed-list!)
31  * - all P2P messages
32  */
33 #include "platform.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_core_service.h"
36 #include "gnunet_datastore_service.h"
37 #include "gnunet_util_lib.h"
38 #include "fs.h"
39
40 /**
41  * Our connection to the datastore.
42  */
43 static struct GNUNET_DATASTORE_Handle *dsh;
44
45 /**
46  * Our scheduler.
47  */
48 static struct GNUNET_SCHEDULER_Handle *sched;
49
50 /**
51  * Our configuration.
52  */
53 const struct GNUNET_CONFIGURATION_Handle *cfg;
54
55
56 /**
57  * In-memory information about indexed files (also available
58  * on-disk).
59  */
60 struct IndexInfo
61 {
62   
63   /**
64    * This is a linked list.
65    */
66   struct IndexInfo *next;
67
68   /**
69    * Name of the indexed file.  Memory allocated
70    * at the end of this struct (do not free).
71    */
72   const char *filename;
73
74   /**
75    * Context for tranmitting confirmation to client,
76    * NULL if we've done this already.
77    */
78   struct GNUNET_SERVER_TransmitContext *tc;
79   
80   /**
81    * Hash of the contents of the file.
82    */
83   GNUNET_HashCode file_id;
84
85 };
86
87
88 /**
89  * Linked list of indexed files.
90  */
91 static struct IndexInfo *indexed_files;
92
93
94 /**
95  * Write the current index information list to disk.
96  */ 
97 static void
98 write_index_list ()
99 {
100   // FIXME
101 }
102
103
104 /**
105  * Read index information from disk.
106  */
107 static void
108 read_index_list ()
109 {
110   // FIXME
111 }
112
113
114 /**
115  * We've validated the hash of the file we're about to
116  * index.  Signal success to the client and update
117  * our internal data structures.
118  *
119  * @param ii the index info entry for the request
120  */
121 static void
122 signal_index_ok (struct IndexInfo *ii)
123 {
124   ii->next = indexed_files;
125   indexed_files = ii;
126   write_index_list ();
127   GNUNET_SERVER_transmit_context_append (ii->tc,
128                                          NULL, 0,
129                                          GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK);
130   GNUNET_SERVER_transmit_context_run (ii->tc,
131                                       GNUNET_TIME_UNIT_MINUTES);
132   ii->tc = NULL;
133 }
134
135
136
137 /**
138  * Function called once the hash computation over an
139  * indexed file has completed.
140  *
141  * @param cls closure, our publishing context
142  * @param res resulting hash, NULL on error
143  */
144 static void 
145 hash_for_index_val (void *cls,
146                     const GNUNET_HashCode *
147                     res)
148 {
149   struct IndexInfo *ii;
150   
151   if ( (res == NULL) ||
152        (0 != memcmp (res,
153                      &ii->file_id,
154                      sizeof(GNUNET_HashCode))) )
155     {
156       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
157                   _("Hash mismatch trying to index file `%s'\n"),
158                   ii->filename);
159       GNUNET_SERVER_transmit_context_append (ii->tc,
160                                              NULL, 0,
161                                              GNUNET_MESSAGE_TYPE_FS_INDEX_START_FAILED);
162       GNUNET_SERVER_transmit_context_run (ii->tc,
163                                           GNUNET_TIME_UNIT_MINUTES);
164       GNUNET_free (ii);
165       return;
166     }
167   signal_index_ok (ii);
168 }
169
170
171 /**
172  * Handle INDEX_START-message.
173  *
174  * @param cls closure
175  * @param client identification of the client
176  * @param message the actual message
177  */
178 static void
179 handle_index_start (void *cls,
180                     struct GNUNET_SERVER_Client *client,
181                     const struct GNUNET_MessageHeader *message)
182 {
183   const struct IndexStartMessage *ism;
184   const char *fn;
185   uint16_t msize;
186   struct IndexInfo *ii;
187   size_t slen;
188   uint32_t dev;
189   uint64_t ino;
190   uint32_t mydev;
191   uint64_t myino;
192
193   msize = ntohs(message->size);
194   if ( (msize <= sizeof (struct IndexStartMessage)) ||
195        ( ((const char *)message)[msize-1] != '\0') )
196     {
197       GNUNET_break (0);
198       GNUNET_SERVER_receive_done (client,
199                                   GNUNET_SYSERR);
200       return;
201     }
202   
203   fn = (const char*) &ism[1];
204   dev = ntohl (ism->device);
205   ino = GNUNET_ntohll (ism->inode);
206   ism = (const struct IndexStartMessage*) message;
207   slen = strlen (fn) + 1;
208   ii = GNUNET_malloc (sizeof (struct IndexInfo) + slen);
209   ii->filename = (const char*) &ii[1];
210   memcpy (&ii[1], fn, slen);
211   ii->file_id = ism->file_id;  
212   ii->tc = GNUNET_SERVER_transmit_context_create (client);
213   if ( ( (dev != 0) ||
214          (ino != 0) ) &&
215        (GNUNET_OK == GNUNET_DISK_file_get_identifiers (fn,
216                                                        &mydev,
217                                                        &myino)) &&
218        ( (dev == mydev) &&
219          (ino == myino) ) )
220     {      
221       /* fast validation OK! */
222       signal_index_ok (ii);
223       return;
224     }
225   /* slow validation, need to hash full file (again) */
226   GNUNET_CRYPTO_hash_file (sched,
227                            GNUNET_SCHEDULER_PRIORITY_IDLE,
228                            GNUNET_NO,
229                            fn,
230                            HASHING_BLOCKSIZE,
231                            &hash_for_index_val,
232                            ii);
233 }
234
235
236 /**
237  * Handle INDEX_LIST_GET-message.
238  *
239  * @param cls closure
240  * @param client identification of the client
241  * @param message the actual message
242  */
243 static void
244 handle_index_list_get (void *cls,
245                        struct GNUNET_SERVER_Client *client,
246                        const struct GNUNET_MessageHeader *message)
247 {
248   struct GNUNET_SERVER_TransmitContext *tc;
249   struct IndexInfoMessage *iim;
250   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
251   size_t slen;
252   const char *fn;
253   struct GNUNET_MessageHeader *msg;
254   struct IndexInfo *pos;
255
256   tc = GNUNET_SERVER_transmit_context_create (client);
257   iim = (struct IndexInfoMessage*) buf;
258   msg = &iim->header;
259   pos = indexed_files;
260   while (NULL != pos)
261     {
262       iim->reserved = 0;
263       iim->file_id = pos->file_id;
264       fn = pos->filename;
265       slen = strlen (fn) + 1;
266       if (slen + sizeof (struct IndexInfoMessage) > 
267           GNUNET_SERVER_MAX_MESSAGE_SIZE)
268         {
269           GNUNET_break (0);
270           break;
271         }
272       memcpy (&iim[1], fn, slen);
273       GNUNET_SERVER_transmit_context_append
274         (tc,
275          &msg[1],
276          sizeof (struct IndexInfoMessage) 
277          - sizeof (struct GNUNET_MessageHeader) + slen,
278          GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY);
279       pos = pos->next;
280     }
281   GNUNET_SERVER_transmit_context_append (tc,
282                                          NULL, 0,
283                                          GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END);
284   GNUNET_SERVER_transmit_context_run (tc,
285                                       GNUNET_TIME_UNIT_MINUTES);
286 }
287
288
289 /**
290  * Handle UNINDEX-message.
291  *
292  * @param cls closure
293  * @param client identification of the client
294  * @param message the actual message
295  */
296 static void
297 handle_unindex (void *cls,
298                 struct GNUNET_SERVER_Client *client,
299                 const struct GNUNET_MessageHeader *message)
300 {
301   const struct UnindexMessage *um;
302   struct IndexInfo *pos;
303   struct IndexInfo *prev;
304   struct IndexInfo *next;
305   struct GNUNET_SERVER_TransmitContext *tc;
306   int found;
307   
308   um = (const struct UnindexMessage*) message;
309   found = GNUNET_NO;
310   prev = NULL;
311   pos = indexed_files;
312   while (NULL != pos)
313     {
314       next = pos->next;
315       if (0 == memcmp (&pos->file_id,
316                        &um->file_id,
317                        sizeof (GNUNET_HashCode)))
318         {
319           if (prev == NULL)
320             indexed_files = pos->next;
321           else
322             prev->next = pos->next;
323           GNUNET_free (pos);
324           found = GNUNET_YES;
325         }
326       else
327         {
328           prev = pos;
329         }
330       pos = next;
331     }
332   if (GNUNET_YES == found)
333     write_index_list ();
334   tc = GNUNET_SERVER_transmit_context_create (client);
335   GNUNET_SERVER_transmit_context_append (tc,
336                                          NULL, 0,
337                                          GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK);
338   GNUNET_SERVER_transmit_context_run (tc,
339                                       GNUNET_TIME_UNIT_MINUTES);
340 }
341
342
343 /**
344  * Signature of a function that is called whenever a datastore
345  * request can be processed (or an entry put on the queue times out).
346  *
347  * @param cls closure
348  * @param ok GNUNET_OK if DS is ready, GNUNET_SYSERR on timeout
349  */
350 typedef void (*RequestFunction)(void *cls,
351                                 int ok);
352
353
354 /**
355  * Doubly-linked list of our requests for the datastore.
356  */
357 struct DatastoreRequestQueue
358 {
359
360   /**
361    * This is a doubly-linked list.
362    */
363   struct DatastoreRequestQueue *next;
364
365   /**
366    * This is a doubly-linked list.
367    */
368   struct DatastoreRequestQueue *prev;
369
370   /**
371    * Function to call (will issue the request).
372    */
373   RequestFunction req;
374
375   /**
376    * Closure for req.
377    */
378   void *req_cls;
379
380   /**
381    * When should this request time-out because we don't care anymore?
382    */
383   struct GNUNET_TIME_Absolute timeout;
384     
385   /**
386    * ID of task used for signaling timeout.
387    */
388   GNUNET_SCHEDULER_TaskIdentifier task;
389
390 };
391
392
393 /**
394  * Head of request queue for the datastore, sorted by timeout.
395  */
396 static struct DatastoreRequestQueue *drq_head;
397
398 /**
399  * Tail of request queue for the datastore.
400  */
401 static struct DatastoreRequestQueue *drq_tail;
402
403
404 /**
405  * Run the next DS request in our
406  * queue, we're done with the current one.
407  */
408 static void
409 next_ds_request ()
410 {
411   struct DatastoreRequestQueue *e;
412   
413   while (NULL != (e = drq_head))
414     {
415       if (0 != GNUNET_TIME_absolute_get_remaining (e->timeout).value)
416         break;
417       if (e->task != GNUNET_SCHEDULER_NO_TASK)
418         GNUNET_SCHEDULER_cancel (sched, e->task);
419       GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
420       e->req (e->req_cls, GNUNET_NO);
421       GNUNET_free (e);  
422     }
423   if (e == NULL)
424     return;
425   if (e->task != GNUNET_SCHEDULER_NO_TASK)
426     GNUNET_SCHEDULER_cancel (sched, e->task);
427   e->task = GNUNET_SCHEDULER_NO_TASK;
428   e->req (e->req_cls, GNUNET_YES);
429   GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
430   GNUNET_free (e);  
431 }
432
433
434 /**
435  * A datastore request had to be timed out. 
436  *
437  * @param cls closure (of type "struct DatastoreRequestQueue*")
438  * @param tc task context, unused
439  */
440 static void
441 timeout_ds_request (void *cls,
442                     const struct GNUNET_SCHEDULER_TaskContext *tc)
443 {
444   struct DatastoreRequestQueue *e = cls;
445
446   e->task = GNUNET_SCHEDULER_NO_TASK;
447   GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
448   e->req (e->req_cls, GNUNET_NO);
449   GNUNET_free (e);  
450 }
451
452
453 /**
454  * Queue a request for the datastore.
455  *
456  * @param deadline by when the request should run
457  * @param fun function to call once the request can be run
458  * @param fun_cls closure for fun
459  */
460 static struct DatastoreRequestQueue *
461 queue_ds_request (struct GNUNET_TIME_Relative deadline,
462                   RequestFunction fun,
463                   void *fun_cls)
464 {
465   struct DatastoreRequestQueue *e;
466   struct DatastoreRequestQueue *bef;
467
468   if (drq_head == NULL)
469     {
470       /* no other requests pending, run immediately */
471       fun (fun_cls, GNUNET_OK);
472       return NULL;
473     }
474   e = GNUNET_malloc (sizeof (struct DatastoreRequestQueue));
475   e->timeout = GNUNET_TIME_relative_to_absolute (deadline);
476   e->req = fun;
477   e->req_cls = fun_cls;
478   if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
479     {
480       /* local request, highest prio, put at head of queue
481          regardless of deadline */
482       bef = NULL;
483     }
484   else
485     {
486       bef = drq_tail;
487       while ( (NULL != bef) &&
488               (e->timeout.value < bef->timeout.value) )
489         bef = bef->prev;
490     }
491   GNUNET_CONTAINER_DLL_insert_after (drq_head, drq_tail, bef, e);
492   if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
493     return e;
494   e->task = GNUNET_SCHEDULER_add_delayed (sched,
495                                           GNUNET_NO,
496                                           GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
497                                           GNUNET_SCHEDULER_NO_TASK,
498                                           deadline,
499                                           &timeout_ds_request,
500                                           e);
501   return e;                                    
502 }
503
504
505 /**
506  * Closure for processing START_SEARCH messages from a client.
507  */
508 struct LocalGetContext
509 {
510
511   /**
512    * This is a doubly-linked list.
513    */
514   struct LocalGetContext *next;
515
516   /**
517    * This is a doubly-linked list.
518    */
519   struct LocalGetContext *prev;
520
521   /**
522    * Client that initiated the search.
523    */
524   struct GNUNET_SERVER_Client *client;
525
526   /**
527    * Array of results that we've already received 
528    * (can be NULL).
529    */
530   GNUNET_HashCode *results; 
531
532   /**
533    * Bloomfilter over all results (for fast query construction);
534    * NULL if we don't have any results.
535    */
536   struct GNUNET_CONTAINER_BloomFilter *results_bf; 
537
538   /**
539    * DS request associated with this operation.
540    */
541   struct DatastoreRequestQueue *req;
542
543   /**
544    * Current result message to transmit to client (or NULL).
545    */
546   struct ContentMessage *result;
547   
548   /**
549    * Type of the content that we're looking for.
550    * 0 for any.
551    */
552   uint32_t type;
553
554   /**
555    * Desired anonymity level.
556    */
557   uint32_t anonymity_level;
558
559   /**
560    * Number of results actually stored in the results array.
561    */
562   unsigned int results_used;
563   
564   /**
565    * Size of the results array in memory.
566    */
567   unsigned int results_size;
568
569   /**
570    * If the request is for a DBLOCK or IBLOCK, this is the identity of
571    * the peer that is known to have a response.  Set to all-zeros if
572    * such a target is not known (note that even if OUR anonymity
573    * level is >0 we may happen to know the responder's identity;
574    * nevertheless, we should probably not use it for a DHT-lookup
575    * or similar blunt actions in order to avoid exposing ourselves).
576    * <p>
577    * If the request is for an SBLOCK, this is the identity of the
578    * pseudonym to which the SBLOCK belongs. 
579    * <p>
580    * If the request is for a KBLOCK, "target" must be all zeros.
581    */
582   GNUNET_HashCode target;
583
584   /**
585    * Hash of the keyword (aka query) for KBLOCKs; Hash of
586    * the CHK-encoded block for DBLOCKS and IBLOCKS (aka query)
587    * and hash of the identifier XORed with the target for
588    * SBLOCKS (aka query).
589    */
590   GNUNET_HashCode query;
591
592 };
593
594
595 /**
596  * Head of doubly-linked LGC list.
597  */
598 static struct LocalGetContext *lgc_head;
599
600 /**
601  * Tail of doubly-linked LGC list.
602  */
603 static struct LocalGetContext *lgc_tail;
604
605
606 /**
607  * Free the state associated with a local get context.
608  *
609  * @param lgc the lgc to free
610  */
611 static void
612 local_get_context_free (struct LocalGetContext *lgc) 
613 {
614   GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
615   GNUNET_SERVER_client_drop (lgc->client); 
616   GNUNET_free_non_null (lgc->results);
617   if (lgc->results_bf != NULL)
618     GNUNET_CONTAINER_bloomfilter_free (lgc->results_bf);
619   if (lgc->req != NULL)
620     {
621       if (lgc->req->task != GNUNET_SCHEDULER_NO_TASK)
622         GNUNET_SCHEDULER_cancel (sched, lgc->req->task);
623       GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
624       GNUNET_free (lgc->req);
625     }
626   GNUNET_free (lgc);
627 }
628
629
630 /**
631  * We're able to transmit the next (local) result to the client.
632  * Do it and ask the datastore for more.  Or, on error, tell
633  * the datastore to stop giving us more.
634  *
635  * @param cls our closure (struct LocalGetContext)
636  * @param max maximum number of bytes we can transmit
637  * @param buf where to copy our message
638  * @return number of bytes copied to buf
639  */
640 static size_t
641 transmit_local_result (void *cls,
642                        size_t max,
643                        void *buf)
644 {
645   struct LocalGetContext *lgc = cls;  
646   uint16_t msize;
647
648   if (NULL == buf)
649     {
650       /* error, abort! */
651       GNUNET_free (lgc->result);
652       lgc->result = NULL;
653       GNUNET_DATASTORE_get_next (dsh, GNUNET_NO);
654       return 0;
655     }
656   msize = ntohs (lgc->result->header.size);
657   GNUNET_assert (max >= msize);
658   memcpy (buf, lgc->result, msize);
659   GNUNET_free (lgc->result);
660   lgc->result = NULL;
661   GNUNET_DATASTORE_get_next (dsh, GNUNET_YES);
662   return msize;
663 }
664
665
666 /**
667  * We're processing (local) results for a search request
668  * from a (local) client.  Pass applicable results to the
669  * client and if we are done either clean up (operation
670  * complete) or switch to P2P search (more results possible).
671  *
672  * @param cls our closure (struct LocalGetContext)
673  * @param key key for the content
674  * @param size number of bytes in data
675  * @param data content stored
676  * @param type type of the content
677  * @param priority priority of the content
678  * @param anonymity anonymity-level for the content
679  * @param expiration expiration time for the content
680  * @param uid unique identifier for the datum;
681  *        maybe 0 if no unique identifier is available
682  */
683 static void
684 process_local_get_result (void *cls,
685                           const GNUNET_HashCode * key,
686                           uint32_t size,
687                           const void *data,
688                           uint32_t type,
689                           uint32_t priority,
690                           uint32_t anonymity,
691                           struct GNUNET_TIME_Absolute
692                           expiration, 
693                           uint64_t uid)
694 {
695   struct LocalGetContext *lgc = cls;
696   size_t msize;
697
698   // FIXME: handle ONDEMAND blocks!
699   if (key == NULL)
700     {
701       /* no further results from datastore; continue
702          processing further requests from the client and
703          allow the next task to use the datastore; also,
704          switch to P2P requests or clean up our state. */
705       next_ds_request ();
706       GNUNET_SERVER_receive_done (lgc->client,
707                                   GNUNET_OK);
708       if ( (lgc->results_used == 0) ||
709            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_KBLOCK) ||
710            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SBLOCK) ||
711            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SKBLOCK) )
712         {
713           // FIXME: initiate P2P search
714           return;
715         }
716       /* got all possible results, clean up! */
717       local_get_context_free (lgc);
718       return;
719     }
720   if (lgc->results_used == lgc->results_size)
721     {
722       GNUNET_array_grow (lgc->results,
723                          lgc->results_size,
724                          lgc->results_size * 2 + 2);
725       if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
726            (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
727         {
728           // FIXME: possibly grow/create BF!
729         }
730     }
731   GNUNET_CRYPTO_hash (data, 
732                       size, 
733                       &lgc->results[lgc->results_used++]);    
734   if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
735        (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
736     {
737       // FIXME: add result to BF!
738     }
739   msize = size + sizeof (struct ContentMessage);
740   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
741   lgc->result = GNUNET_malloc (msize);
742   lgc->result->header.size = htons (msize);
743   lgc->result->header.type = htons (GNUNET_MESSAGE_TYPE_FS_CONTENT);
744   lgc->result->type = htonl (type);
745   lgc->result->expiration = GNUNET_TIME_absolute_hton (expiration);
746   memcpy (&lgc->result[1],
747           data,
748           size);
749   GNUNET_SERVER_notify_transmit_ready (lgc->client,
750                                        msize,
751                                        GNUNET_TIME_UNIT_FOREVER_REL,
752                                        &transmit_local_result,
753                                        lgc);
754 }
755
756
757 /**
758  * We're processing a search request from a local
759  * client.  Now it is our turn to query the datastore.
760  * 
761  * @param cls our closure (struct LocalGetContext)
762  * @param tc unused
763  */
764 static void
765 transmit_local_get (void *cls,
766                     const struct GNUNET_SCHEDULER_TaskContext *tc)
767 {
768   struct LocalGetContext *lgc = cls;
769
770   // FIXME: handle ONDEMAND blocks (change type to ANY for DBLOCK/IBLOCK)
771   GNUNET_DATASTORE_get (dsh,
772                         &lgc->query,
773                         lgc->type,
774                         &process_local_get_result,
775                         lgc,
776                         GNUNET_TIME_UNIT_FOREVER_REL);
777 }
778
779
780 /**
781  * We're processing a search request from a local
782  * client.  Now it is our turn to query the datastore.
783  * 
784  * @param cls our closure (struct LocalGetContext)
785  * @param ok did we succeed to queue for datastore access, should always be GNUNET_OK
786  */
787 static void 
788 transmit_local_get_ready (void *cls,
789                           int ok)
790 {
791   struct LocalGetContext *lgc = cls;
792
793   GNUNET_assert (GNUNET_OK == ok);
794   GNUNET_SCHEDULER_add_continuation (sched,
795                                      GNUNET_NO,
796                                      &transmit_local_get,
797                                      lgc,
798                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
799 }
800
801
802 /**
803  * Handle START_SEARCH-message (search request from client).
804  *
805  * @param cls closure
806  * @param client identification of the client
807  * @param message the actual message
808  */
809 static void
810 handle_start_search (void *cls,
811                      struct GNUNET_SERVER_Client *client,
812                      const struct GNUNET_MessageHeader *message)
813 {
814   const struct SearchMessage *sm;
815   struct LocalGetContext *lgc;
816
817   sm = (const struct SearchMessage*) message;
818   GNUNET_SERVER_client_keep (client);
819   lgc = GNUNET_malloc (sizeof (struct LocalGetContext));
820   lgc->client = client;
821   lgc->type = ntohl (sm->type);
822   lgc->anonymity_level = ntohl (sm->anonymity_level);
823   lgc->target = sm->target;
824   lgc->query = sm->query;
825   GNUNET_CONTAINER_DLL_insert (lgc_head, lgc_tail, lgc);
826   lgc->req = queue_ds_request (GNUNET_TIME_UNIT_FOREVER_REL,
827                                &transmit_local_get_ready,
828                                lgc);
829 }
830
831
832 /**
833  * List of handlers for the messages understood by this
834  * service.
835  */
836 static struct GNUNET_SERVER_MessageHandler handlers[] = {
837   {&handle_index_start, NULL, 
838    GNUNET_MESSAGE_TYPE_FS_INDEX_START, 0},
839   {&handle_index_list_get, NULL, 
840    GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET, sizeof(struct GNUNET_MessageHeader) },
841   {&handle_unindex, NULL, GNUNET_MESSAGE_TYPE_FS_UNINDEX, 
842    sizeof (struct UnindexMessage) },
843   {&handle_start_search, NULL, GNUNET_MESSAGE_TYPE_FS_START_SEARCH, 
844    sizeof (struct SearchMessage) },
845   {NULL, NULL, 0, 0}
846 };
847
848
849 /**
850  * A client disconnected.  Remove all of its pending queries.
851  *
852  * @param cls closure, NULL
853  * @param client identification of the client
854  */
855 static void
856 handle_client_disconnect (void *cls,
857                           struct GNUNET_SERVER_Client
858                           * client)
859 {
860   struct LocalGetContext *lgc;
861
862   lgc = lgc_head;
863   while ( (NULL != lgc) &&
864           (lgc->client != client) )
865     lgc = lgc->next;
866   if (lgc == NULL)
867     return; /* not one of our clients */
868   local_get_context_free (lgc);
869 }
870
871
872 /**
873  * Task run during shutdown.
874  *
875  * @param cls unused
876  * @param tc unused
877  */
878 static void
879 shutdown_task (void *cls,
880                const struct GNUNET_SCHEDULER_TaskContext *tc)
881 {
882   GNUNET_DATASTORE_disconnect (dsh,
883                                GNUNET_NO);
884   dsh = NULL;
885 }
886
887
888 /**
889  * Process fs requests.
890  *
891  * @param cls closure
892  * @param sched scheduler to use
893  * @param server the initialized server
894  * @param cfg configuration to use
895  */
896 static void
897 run (void *cls,
898      struct GNUNET_SCHEDULER_Handle *s,
899      struct GNUNET_SERVER_Handle *server,
900      const struct GNUNET_CONFIGURATION_Handle *c)
901 {
902   read_index_list ();
903   sched = s;
904   cfg = c;
905   dsh = GNUNET_DATASTORE_connect (cfg,
906                                   sched);
907   if (NULL == dsh)
908     {
909       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
910                   _("Failed to connect to datastore service.\n"));
911       return;
912     }
913   GNUNET_SERVER_disconnect_notify (server, 
914                                    &handle_client_disconnect,
915                                    NULL);
916   GNUNET_SERVER_add_handlers (server, handlers);
917   // FIXME: also register with core to handle P2P messages!
918   GNUNET_SCHEDULER_add_delayed (sched,
919                                 GNUNET_YES,
920                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
921                                 GNUNET_SCHEDULER_NO_TASK,
922                                 GNUNET_TIME_UNIT_FOREVER_REL,
923                                 &shutdown_task,
924                                 NULL);
925 }
926
927
928 /**
929  * The main function for the fs service.
930  *
931  * @param argc number of arguments from the command line
932  * @param argv command line arguments
933  * @return 0 ok, 1 on error
934  */
935 int
936 main (int argc, char *const *argv)
937 {
938   return (GNUNET_OK ==
939           GNUNET_SERVICE_run (argc,
940                               argv,
941                               "fs", &run, NULL, NULL, NULL)) ? 0 : 1;
942 }
943
944 /* end of gnunet-service-fs.c */