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 }