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 }