libgnunetchat

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

gnunet_chat_file.c (8901B)


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