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 }