libgnunetchat

library for GNUnet Messenger
Log | Files | Refs | README | LICENSE

gnunet_chat_file.c (9760B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2021--2024 GNUnet e.V.
      4 
      5    GNUnet is free software: you can redistribute it and/or modify it
      6    under the terms of the GNU Affero General Public License as published
      7    by the Free Software Foundation, either version 3 of the License,
      8    or (at your 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    Affero General Public License for more details.
     14 
     15    You should have received a copy of the GNU Affero General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18    SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 /*
     21  * @author Tobias Frisch
     22  * @file gnunet_chat_file.c
     23  */
     24 
     25 #include "gnunet_chat_file.h"
     26 
     27 #include "gnunet_chat_context.h"
     28 #include "gnunet_chat_handle.h"
     29 
     30 #include <gnunet/gnunet_common.h>
     31 #include <gnunet/gnunet_fs_service.h>
     32 #include <string.h>
     33 
     34 static void
     35 file_initialize (struct GNUNET_CHAT_File *file)
     36 {
     37   GNUNET_assert(file);
     38 
     39   file->download = NULL;
     40   file->publish = NULL;
     41   file->unindex = NULL;
     42 
     43   file->upload_head = NULL;
     44   file->upload_tail = NULL;
     45 
     46   file->download_head = NULL;
     47   file->download_tail = NULL;
     48 
     49   file->unindex_head = NULL;
     50   file->unindex_tail = NULL;
     51 
     52   file->status = 0;
     53   file->preview = NULL;
     54 
     55   file->user_pointer = NULL;
     56 }
     57 
     58 struct GNUNET_CHAT_File*
     59 file_create_from_message (struct GNUNET_CHAT_Handle *handle,
     60 			                    const struct GNUNET_MESSENGER_MessageFile *message)
     61 {
     62   GNUNET_assert((handle) && (message) && (message->uri));
     63 
     64   struct GNUNET_CHAT_File* file = GNUNET_new(struct GNUNET_CHAT_File);
     65 
     66   if (!file)
     67     return NULL;
     68 
     69   file->handle = handle;
     70   file->name = GNUNET_strndup(message->name, NAME_MAX);
     71 
     72   file->key = GNUNET_new(struct GNUNET_CRYPTO_SymmetricSessionKey);
     73 
     74   if (!(file->key))
     75   {
     76     GNUNET_free(file);
     77     return NULL;
     78   }
     79 
     80   GNUNET_memcpy(file->key, &(message->key),
     81                 sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
     82   GNUNET_memcpy(&(file->hash), &(message->hash), sizeof(file->hash));
     83 
     84   file->meta = GNUNET_FS_meta_data_create();
     85   file->uri = GNUNET_FS_uri_parse(message->uri, NULL);
     86 
     87   file_initialize(file);
     88 
     89   return file;
     90 }
     91 
     92 struct GNUNET_CHAT_File*
     93 file_create_from_chk_uri (struct GNUNET_CHAT_Handle *handle,
     94                           const struct GNUNET_FS_Uri *uri)
     95 {
     96   GNUNET_assert((handle) && (uri));
     97 
     98   const struct GNUNET_HashCode *hash = GNUNET_FS_uri_chk_get_file_hash(uri);
     99 
    100   if (!hash)
    101     return NULL;
    102 
    103   struct GNUNET_CHAT_File* file = GNUNET_new(struct GNUNET_CHAT_File);
    104 
    105   if (!file)
    106     return NULL;
    107 
    108   file->handle = handle;
    109   file->name = NULL;
    110 
    111   file->key = NULL;
    112 
    113   GNUNET_memcpy(&(file->hash), hash, sizeof(file->hash));
    114 
    115   file->meta = GNUNET_FS_meta_data_create();
    116   file->uri = GNUNET_FS_uri_dup(uri);
    117   
    118   file_initialize(file);
    119 
    120   return file;
    121 }
    122 
    123 struct GNUNET_CHAT_File*
    124 file_create_from_disk (struct GNUNET_CHAT_Handle *handle,
    125                        const char *name,
    126                        const struct GNUNET_HashCode *hash,
    127                        const struct GNUNET_CRYPTO_SymmetricSessionKey *key)
    128 {
    129   GNUNET_assert((handle) && (name) && (hash));
    130 
    131   struct GNUNET_CHAT_File* file = GNUNET_new(struct GNUNET_CHAT_File);
    132 
    133   if (!file)
    134     return NULL;
    135 
    136   file->handle = handle;
    137   file->name = GNUNET_strndup(name, NAME_MAX);
    138 
    139   if (!key)
    140   {
    141     file->key = NULL;
    142     goto skip_key;
    143   }
    144 
    145   file->key = GNUNET_new(struct GNUNET_CRYPTO_SymmetricSessionKey);
    146 
    147   if (!(file->key))
    148   {
    149     GNUNET_free(file);
    150     return NULL;
    151   }
    152 
    153   GNUNET_memcpy(file->key, key,
    154                 sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
    155 
    156 skip_key:
    157   GNUNET_memcpy(&(file->hash), hash, sizeof(file->hash));
    158 
    159   file->meta = GNUNET_FS_meta_data_create();
    160   file->uri = NULL;
    161 
    162   file_initialize(file);
    163 
    164   return file;
    165 }
    166 
    167 void
    168 file_destroy (struct GNUNET_CHAT_File *file)
    169 {
    170   GNUNET_assert(file);
    171 
    172   struct GNUNET_CHAT_FileUpload *upload;
    173   struct GNUNET_CHAT_FileDownload *download;
    174   struct GNUNET_CHAT_FileUnindex *unindex;
    175 
    176   if (!(file->preview))
    177     goto skip_preview;
    178 
    179   if (!(file->key))
    180     goto skip_filename;
    181 
    182   char *filename = handle_create_file_path(
    183     file->handle, &(file->hash)
    184   );
    185 
    186   if (!filename)
    187     goto skip_filename;
    188 
    189   if (0 != strcmp(filename, file->preview))
    190     remove(file->preview);
    191 
    192   GNUNET_free(filename);
    193 
    194 skip_filename:
    195   GNUNET_free(file->preview);
    196 
    197 skip_preview:
    198   if (file->publish)
    199     GNUNET_FS_publish_stop(file->publish);
    200 
    201   if (file->download)
    202     GNUNET_FS_download_stop(file->download, GNUNET_NO);
    203 
    204   if (file->unindex)
    205     GNUNET_FS_unindex_stop(file->unindex);
    206 
    207   while (file->upload_head)
    208   {
    209     upload = file->upload_head;
    210 
    211     GNUNET_CONTAINER_DLL_remove(
    212       file->upload_head,
    213       file->upload_tail,
    214       upload
    215     );
    216 
    217     GNUNET_free(upload);
    218   }
    219 
    220   while (file->download_head)
    221   {
    222     download = file->download_head;
    223 
    224     GNUNET_CONTAINER_DLL_remove(
    225       file->download_head,
    226       file->download_tail,
    227       download
    228     );
    229 
    230     GNUNET_free(download);
    231   }
    232 
    233   while (file->unindex_head)
    234   {
    235     unindex = file->unindex_head;
    236 
    237     GNUNET_CONTAINER_DLL_remove(
    238       file->unindex_head,
    239       file->unindex_tail,
    240       unindex
    241     );
    242 
    243     GNUNET_free(unindex);
    244   }
    245 
    246   if (file->uri)
    247     GNUNET_FS_uri_destroy(file->uri);
    248 
    249   if (file->meta)
    250     GNUNET_FS_meta_data_destroy(file->meta);
    251 
    252   if (file->key)
    253     GNUNET_free(file->key);
    254 
    255   if (file->name)
    256     GNUNET_free(file->name);
    257 
    258   GNUNET_free(file);
    259 }
    260 
    261 void
    262 file_bind_upload (struct GNUNET_CHAT_File *file,
    263                   struct GNUNET_CHAT_Context *context,
    264                   GNUNET_CHAT_FileUploadCallback cb,
    265                   void *cls)
    266 {
    267   GNUNET_assert(file);
    268 
    269   struct GNUNET_CHAT_FileUpload *upload = GNUNET_new(
    270     struct GNUNET_CHAT_FileUpload
    271   );
    272 
    273   upload->context = context;
    274   upload->callback = cb;
    275   upload->cls = cls;
    276 
    277   GNUNET_CONTAINER_DLL_insert(
    278     file->upload_head,
    279     file->upload_tail,
    280     upload
    281   );
    282 }
    283 
    284 void
    285 file_bind_downlaod (struct GNUNET_CHAT_File *file,
    286                     GNUNET_CHAT_FileDownloadCallback cb,
    287                     void *cls)
    288 {
    289   GNUNET_assert(file);
    290 
    291   struct GNUNET_CHAT_FileDownload *download = GNUNET_new(
    292     struct GNUNET_CHAT_FileDownload
    293   );
    294 
    295   download->callback = cb;
    296   download->cls = cls;
    297 
    298   GNUNET_CONTAINER_DLL_insert(
    299     file->download_head,
    300     file->download_tail,
    301     download
    302   );
    303 }
    304 
    305 void
    306 file_bind_unindex (struct GNUNET_CHAT_File *file,
    307                    GNUNET_CHAT_FileUnindexCallback cb,
    308                    void *cls)
    309 {
    310   GNUNET_assert(file);
    311 
    312   struct GNUNET_CHAT_FileUnindex *unindex = GNUNET_new(
    313     struct GNUNET_CHAT_FileUnindex
    314   );
    315 
    316   unindex->callback = cb;
    317   unindex->cls = cls;
    318 
    319   GNUNET_CONTAINER_DLL_insert(
    320     file->unindex_head,
    321     file->unindex_tail,
    322     unindex
    323   );
    324 }
    325 
    326 void
    327 file_update_upload (struct GNUNET_CHAT_File *file,
    328                     uint64_t completed,
    329                     uint64_t size)
    330 {
    331   GNUNET_assert(file);
    332 
    333   file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
    334 
    335   struct GNUNET_CHAT_FileUpload *upload = file->upload_head;
    336 
    337   while (upload)
    338   {
    339     if (upload->callback)
    340       upload->callback(upload->cls, file, completed, size);
    341 
    342     upload = upload->next;
    343   }
    344 
    345   if (!(file->uri))
    346     return;
    347 
    348   struct GNUNET_MESSENGER_Message msg;
    349   memset(&msg, 0, sizeof(msg));
    350 
    351   msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
    352 
    353   if (file->key)
    354     GNUNET_memcpy(&(msg.body.file.key), file->key,
    355                   sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
    356 
    357   GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash));
    358   GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX);
    359   msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri);
    360 
    361   while (file->upload_head)
    362   {
    363     upload = file->upload_head;
    364 
    365     if (upload->context)
    366       GNUNET_MESSENGER_send_message(upload->context->room, &msg, NULL);
    367 
    368     GNUNET_CONTAINER_DLL_remove(
    369       file->upload_head,
    370       file->upload_tail,
    371       upload
    372     );
    373 
    374     GNUNET_free(upload);
    375   }
    376 
    377   GNUNET_free(msg.body.file.uri);
    378 
    379   file->status &= (
    380     GNUNET_CHAT_FILE_STATUS_MASK ^ GNUNET_CHAT_FILE_STATUS_PUBLISH
    381   );
    382 }
    383 
    384 void
    385 file_update_download (struct GNUNET_CHAT_File *file,
    386                       uint64_t completed,
    387                       uint64_t size)
    388 {
    389   GNUNET_assert(file);
    390 
    391   file->status |= GNUNET_CHAT_FILE_STATUS_DOWNLOAD;
    392 
    393   struct GNUNET_CHAT_FileDownload *download = file->download_head;
    394 
    395   while (download)
    396   {
    397     if (download->callback)
    398       download->callback(download->cls, file, completed, size);
    399 
    400     download = download->next;
    401   }
    402 
    403   if (completed < size)
    404     return;
    405 
    406   while (file->download_head)
    407   {
    408     download = file->download_head;
    409 
    410     GNUNET_CONTAINER_DLL_remove(
    411       file->download_head,
    412       file->download_tail,
    413       download
    414     );
    415 
    416     GNUNET_free(download);
    417   }
    418 
    419   file->status &= (
    420     GNUNET_CHAT_FILE_STATUS_MASK ^ GNUNET_CHAT_FILE_STATUS_DOWNLOAD
    421   );
    422 }
    423 
    424 void
    425 file_update_unindex (struct GNUNET_CHAT_File *file,
    426                      uint64_t completed,
    427                      uint64_t size)
    428 {
    429   GNUNET_assert(file);
    430 
    431   file->status |= GNUNET_CHAT_FILE_STATUS_UNINDEX;
    432 
    433   struct GNUNET_CHAT_FileUnindex *unindex = file->unindex_head;
    434 
    435   while (unindex)
    436   {
    437     if (unindex->callback)
    438       unindex->callback(unindex->cls, file, completed, size);
    439 
    440     unindex = unindex->next;
    441   }
    442 
    443   if (completed < size)
    444     return;
    445 
    446   while (file->unindex_head)
    447   {
    448     unindex = file->unindex_head;
    449 
    450     GNUNET_CONTAINER_DLL_remove(
    451       file->unindex_head,
    452       file->unindex_tail,
    453       unindex
    454     );
    455 
    456     GNUNET_free(unindex);
    457   }
    458 
    459   file->status &= (
    460     GNUNET_CHAT_FILE_STATUS_MASK ^ GNUNET_CHAT_FILE_STATUS_UNINDEX
    461   );
    462 }