libgnunetchat

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

gnunet_chat_file.c (8931B)


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