ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_app_direct.c
Go to the documentation of this file.
1
9#include "mdl_app_internal.h"
12
20
39 const char* first,
40 const char* second,
41 const char* third)
42{
43 ra8_err_t error = priv_mdl_stream_text(k_ra8_ok, stream, first);
44 error = priv_mdl_stream_text(error, stream, second);
45 return priv_mdl_stream_text(error, stream, third);
46}
47
60{
61 if ((error != k_ra8_ok) && (priv_mdl_app_context()->io_error == k_ra8_ok)) {
63 }
64}
65
83internal_direct_pack_failure(const char* dir, const char* ext, ra8_err_t failure)
84{
86 ra8_err_t error = internal_direct_text3(diagnostic, "mdl: pack '", dir, "' as .");
87 error = priv_mdl_stream_text(error, diagnostic, ext);
88 error = priv_mdl_stream_text(error, diagnostic, " FAILED (err 0x");
89 error = priv_mdl_stream_hex(error, diagnostic, (uint32_t)failure);
90 return priv_mdl_stream_text(error, diagnostic, ")\n");
91}
92
108{
110 if ((sink == nullptr) || (sink->storage == nullptr) || (sink->destination == nullptr)) {
112 }
113 if (sink->output.writer.transaction.active) {
114 const ra8_err_t aborted = priv_mdl_export_output_abort(&sink->output);
115 if (aborted != k_ra8_ok) {
116 return aborted;
117 }
118 }
120 sink->storage,
121 sink->destination,
122 sink->format);
123}
124
143 const uint8_t* bytes,
144 uint32_t length,
145 uint32_t* out_written)
146{
148 if ((sink == nullptr) || (out_written == nullptr) || ((bytes == nullptr) && (length != 0U))) {
150 }
151 *out_written = 0U;
152 const ra8_err_t err = priv_mdl_export_output_write(&sink->output, bytes, length);
153 if (err == k_ra8_ok) {
154 *out_written = length;
155 }
156 return err;
157}
158
174RA8_INTERNAL static uint32_t internal_max_u32(uint32_t a, uint32_t b)
175{
176 return (a > b) ? a : b;
177}
178
194RA8_INTERNAL static void
195internal_remove_stale_page_variants(const char* out_dir, size_t page_no, const char* true_ext)
196{
197 static const char* const alternate_exts[] = {"jpg", "jpeg", "png", "webp", "gif", "bmp"};
198 for (size_t ext_idx = 0U; ext_idx < (sizeof(alternate_exts) / sizeof(alternate_exts[0]));
199 ++ext_idx) {
200 if (strcmp(alternate_exts[ext_idx], true_ext) == 0) {
201 continue;
202 }
203 char alternate_leaf[k_leaf_name_bytes] = {};
204 const int alternate_len = snprintf(alternate_leaf,
205 sizeof(alternate_leaf),
206 "page_%04zu.%s",
207 page_no,
208 alternate_exts[ext_idx]);
209 char alternate_path[k_file_path_bytes];
210 if ((alternate_len <= 0) || ((size_t)alternate_len >= sizeof(alternate_leaf)) ||
211 !mdl_path_join(out_dir, alternate_leaf, alternate_path, sizeof(alternate_path))) {
213 "mdl: warning: cannot resolve stale page "
214 "variant '",
215 alternate_leaf,
216 "'\n"));
217 if (priv_mdl_app_context()->io_error != k_ra8_ok) {
218 return;
219 }
220 continue;
221 }
222 bool removed = false;
224 alternate_path,
225 &removed);
226 (void)removed;
227 if (error != k_ra8_ok) {
229 "mdl: warning: cannot safely remove stale "
230 "page variant '",
231 alternate_path,
232 "'\n"));
233 if (priv_mdl_app_context()->io_error != k_ra8_ok) {
234 return;
235 }
236 }
237 }
238}
239
259RA8_INTERNAL static size_t
260internal_publish_page_image(mdl_fetch_body_t* body, const char* out_dir, size_t idx)
261{
263 if (error != k_ra8_ok) {
264 (void)priv_mdl_fetch_body_abort(body);
265 ra8_err_t output_error =
266 priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->diagnostic, " page ");
267 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->diagnostic, idx + 1U);
268 output_error =
269 priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, " FAILED ");
270 output_error = priv_mdl_stream_text(output_error,
271 priv_mdl_app_context()->diagnostic,
272 priv_mdl_app_context()->images.urls[idx]);
273 output_error = priv_mdl_stream_text(output_error,
274 priv_mdl_app_context()->diagnostic,
275 " -- unsupported image signature\n");
276 internal_direct_latch(output_error);
277 return 1U;
278 }
279 const char* const dot = strrchr(body->actual_abs, '.');
280 if ((dot == nullptr) || (dot[1] == '\0')) {
281 (void)priv_mdl_fetch_body_abort(body);
282 return 1U;
283 }
284 char true_ext[k_ext_bytes];
285 const int extension = snprintf(true_ext, sizeof(true_ext), "%s", dot + 1);
286 if ((extension <= 0) || ((size_t)extension >= sizeof(true_ext))) {
287 (void)priv_mdl_fetch_body_abort(body);
288 return 1U;
289 }
290 error = priv_mdl_fetch_body_commit(body);
291 if (error != k_ra8_ok) {
292 ra8_err_t output_error =
293 priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->diagnostic, " page ");
294 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->diagnostic, idx + 1U);
295 output_error = priv_mdl_stream_text(output_error,
296 priv_mdl_app_context()->diagnostic,
297 " FAILED -- atomic publication failed\n");
298 internal_direct_latch(output_error);
299 return 1U;
300 }
301 internal_remove_stale_page_variants(out_dir, idx + 1U, true_ext);
302 return (priv_mdl_app_context()->io_error == k_ra8_ok) ? 0U : 1U;
303}
304
328RA8_INTERNAL static size_t internal_download_page_image(const char* url,
329 const char* out_dir,
330 uint32_t dmin,
331 uint32_t dmax,
332 uint32_t timeout,
333 mdl_politeness_t* pol,
334 size_t idx)
335{
336 uint32_t crawl = 0U;
338 priv_mdl_app_context()->images.urls[idx],
339 &crawl)) {
340 return 1U;
341 }
342 (void)mdl_politeness_wait(pol, internal_max_u32(dmin, crawl), internal_max_u32(dmax, crawl));
343 char holding_leaf[k_leaf_name_bytes];
344 const int holding_len =
345 snprintf(holding_leaf, sizeof(holding_leaf), "page_%04zu.download", idx + 1U);
346 char holding[k_file_path_bytes];
347 if ((holding_len < 0) || ((size_t)holding_len >= sizeof(holding_leaf)) ||
348 !mdl_path_join(out_dir, holding_leaf, holding, sizeof(holding))) {
349 return 1U;
350 }
351 const mdl_net_req_t ir = {.user_agent = priv_mdl_app_context()->session.user_agent,
352 .referer = url,
353 .timeout_ms = timeout};
354 size_t got = 0U;
355 mdl_net_resp_t resp = {};
356 mdl_fetch_body_t body = {};
357 ra8_err_t rc =
358 priv_mdl_fetch_body_init_image(&body, &priv_mdl_app_context()->storage, holding, holding_leaf);
360 if (rc == k_ra8_ok) {
361 rc = mdl_net_get_body(priv_mdl_app_context()->session.net,
362 priv_mdl_app_context()->images.urls[idx],
363 &ir,
364 &sink,
365 &got,
366 &resp);
367 }
368 if (rc != k_ra8_ok) {
369 (void)priv_mdl_fetch_body_abort(&body);
370 char reason[k_mdl_reason_max];
371 priv_mdl_fetch_reason(rc, resp.status, reason, sizeof(reason));
372 ra8_err_t output_error =
373 priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->diagnostic, " page ");
374 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->diagnostic, idx + 1U);
375 output_error =
376 priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, " FAILED ");
377 output_error = priv_mdl_stream_text(output_error,
378 priv_mdl_app_context()->diagnostic,
379 priv_mdl_app_context()->images.urls[idx]);
380 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, " -- ");
381 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, reason);
382 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->diagnostic, "\n");
383 internal_direct_latch(output_error);
384 return 1U;
385 }
386 return internal_publish_page_image(&body, out_dir, idx);
387}
388
410RA8_INTERNAL static size_t internal_download_page_images(const char* url,
411 const char* out_dir,
412 uint32_t max_imgs,
413 uint64_t seed,
414 uint32_t timeout,
415 bool polite)
416{
418 mdl_politeness_init(&pol, seed);
419 const uint32_t dmin = polite ? (uint32_t)k_polite_img_min_ms : (uint32_t)k_page_img_delay_min;
420 const uint32_t dmax = polite ? (uint32_t)k_polite_img_max_ms : (uint32_t)k_page_img_delay_max;
421 const size_t limit = (max_imgs == 0U) ? priv_mdl_app_context()->images.count : (size_t)max_imgs;
422 size_t fail = 0U;
423 for (size_t i = 0U; (i < priv_mdl_app_context()->images.count) && (i < limit); ++i) {
424 fail += internal_download_page_image(url, out_dir, dmin, dmax, timeout, &pol, i);
425 if (priv_mdl_app_context()->io_error != k_ra8_ok) {
426 return fail;
427 }
428 }
429 ra8_err_t output_error = priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->output, "done: ");
430 output_error = priv_mdl_stream_u64(
431 output_error,
432 priv_mdl_app_context()->output,
433 (priv_mdl_app_context()->images.count < limit ? priv_mdl_app_context()->images.count : limit) -
434 fail);
435 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " ok, ");
436 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->output, fail);
437 output_error =
438 priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " failed, into ");
439 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, out_dir);
440 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, "/\n");
441 internal_direct_latch(output_error);
442 return fail;
443}
444
462RA8_INTERNAL static bool internal_prepare_output_dir(const char* out_dir, char* out_abs)
463{
465 k_ra8_ok) {
467 "mdl: cannot create output directory '",
468 out_dir,
469 "'\n"));
470 return false;
471 }
472 const int copied = snprintf(out_abs, (size_t)k_fw_fs_path_cap, "%s", out_dir);
473 if ((copied <= 0) || ((size_t)copied >= (size_t)k_fw_fs_path_cap)) {
475 "mdl: output path is not a directory: '",
476 out_dir,
477 "'\n"));
478 return false;
479 }
480 return true;
481}
482
502RA8_INTERNAL static bool
503internal_prepare_artifact_path(const char* out_dir, const char* leaf, char* final_path)
504{
506 k_ra8_ok) {
508 "mdl: cannot create output directory '",
509 out_dir,
510 "'\n"));
511 return false;
512 }
513 if (!mdl_path_join(out_dir, leaf, final_path, (size_t)k_fw_fs_path_cap)) {
515 "mdl: unsafe or unresolved output directory '",
516 out_dir,
517 "'\n"));
518 return false;
519 }
520 return true;
521}
522
541RA8_INTERNAL static bool
543{
544 if (error == k_ra8_ok) {
545 return true;
546 }
547 if (state->output.writer.transaction.active) {
548 const ra8_err_t aborted = priv_mdl_export_output_abort(&state->output);
549 if (aborted != k_ra8_ok) {
550 error = aborted;
551 }
552 }
553 if (error == k_ra8_err_validation_failed) {
555 priv_mdl_app_context()->diagnostic,
556 "mdl: downloaded artifact failed structural "
557 "validation\n"));
558 return false;
559 }
560 char reason[k_mdl_reason_max];
561 priv_mdl_fetch_reason(error, status, reason, sizeof(reason));
563 "mdl: artifact fetch failed -- ",
564 reason,
565 "\n"));
566 return false;
567}
568
590RA8_INTERNAL static bool internal_fetch_artifact(const char* url,
591 const char* final_path,
592 mdl_format_t format,
593 uint32_t timeout,
594 const mdl_run_opts_t* opts,
595 size_t* got)
596{
597 mdl_net_iface_t net = {};
598 if (mdl_net_provider_open(opts->net, &opts->policy, &net) != k_ra8_ok) {
600 priv_mdl_app_context()->diagnostic,
601 "mdl: network init failed\n"));
602 return false;
603 }
604 char ua[k_mdl_ua_max];
605 if (priv_mdl_app_start_session(&net, opts, nullptr, ua, sizeof(ua)) != k_ra8_ok) {
606 mdl_net_destroy(&net);
607 return false;
608 }
609 if (!mdl_session_url_allowed(&priv_mdl_app_context()->session, url, nullptr)) {
610 mdl_net_destroy(&net);
611 return false;
612 }
613 const mdl_net_req_t req = {.user_agent = priv_mdl_app_context()->session.user_agent,
614 .referer = nullptr,
615 .timeout_ms = timeout};
616 mdl_net_resp_t resp = {};
618 .destination = final_path,
619 .format = format};
622 .ctx = &state};
623 ra8_err_t rc = mdl_net_get_body(&net, url, &req, &sink, got, &resp);
624 if (rc == k_ra8_ok) {
625 bool published = false;
626 rc =
627 priv_mdl_export_output_commit(&state.output, &priv_mdl_app_context()->export_ws, &published);
628 if ((rc == k_ra8_ok) && !published) {
630 }
631 }
632 mdl_net_destroy(&net);
633 return internal_finish_artifact_fetch(&state, rc, resp.status);
634}
635
636int mdl_app_run_artifact(const char* url,
637 const char* out_dir,
638 uint32_t timeout,
639 const mdl_run_opts_t* opts)
640{
641 char leaf[k_leaf_name_bytes];
642 mdl_urlname_last_segment(url, leaf, sizeof(leaf));
644 if ((mdl_format_from_path(leaf, &format) != k_ra8_ok) || !mdl_format_is_verifiable(format)) {
646 "mdl: direct artifact '",
647 leaf,
648 "' has no supported structural validator "
649 "(cbz|cbt|cbt.gz|epub|jof|rabook)\n"));
650 return 1;
651 }
652 char final_path[k_fw_fs_path_cap];
653 if (!internal_prepare_artifact_path(out_dir, leaf, final_path)) {
654 return 1;
655 }
656 size_t got = 0U;
657 if (!internal_fetch_artifact(url, final_path, format, timeout, opts, &got)) {
658 return 1;
659 }
660 mdl_verify_report_t report = {};
661 const ra8_err_t verify_rc = mdl_verify_file(&priv_mdl_app_context()->storage,
662 format,
663 final_path,
664 &priv_mdl_app_context()->export_ws,
665 &report);
666 if (verify_rc != k_ra8_ok) {
668 "downloaded and verified ",
669 final_path,
670 " (");
671 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->output, got);
672 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " bytes)\n");
673 return (output_error == k_ra8_ok) ? 0 : 1;
674 }
676 "downloaded and verified ",
677 final_path,
678 " (");
679 output_error = priv_mdl_stream_u64(output_error, priv_mdl_app_context()->output, got);
680 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " bytes, ");
681 output_error =
682 priv_mdl_stream_u64(output_error, priv_mdl_app_context()->output, report.page_count);
683 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " page(s))\n");
684 return (output_error == k_ra8_ok) ? 0 : 1;
685}
686
705RA8_INTERNAL static bool
706internal_extract_page_images(const char* url, const char* attr, uint32_t timeout)
707{
708 const mdl_net_req_t req = {.user_agent = priv_mdl_app_context()->session.user_agent,
709 .referer = nullptr,
710 .timeout_ms = timeout};
711 size_t len = 0U;
712 mdl_net_resp_t resp = {};
713 ra8_err_t error = mdl_net_get_buf(priv_mdl_app_context()->session.net,
714 url,
715 &req,
716 priv_mdl_app_context()->page,
717 sizeof(priv_mdl_app_context()->page),
718 &len,
719 &resp);
720 if (error != k_ra8_ok) {
721 char reason[k_mdl_reason_max];
722 priv_mdl_fetch_reason(error, resp.status, reason, sizeof(reason));
724 "mdl: fetch failed -- ",
725 reason,
726 "\n"));
727 return false;
728 }
730 len,
731 url,
732 attr,
733 nullptr,
734 &priv_mdl_app_context()->images);
735 if ((error == k_ra8_ok) && (priv_mdl_app_context()->images.count > 0U)) {
736 return true;
737 }
739 priv_mdl_app_context()->diagnostic,
740 "mdl: page contains no supported image URLs\n"));
741 return false;
742}
743
744int mdl_app_run_page(const char* url,
745 const char* out_dir,
746 const char* attr,
747 uint32_t max_imgs,
748 uint64_t seed,
749 uint32_t timeout,
750 const mdl_run_opts_t* opts)
751{
752 char out_abs[k_fw_fs_path_cap];
753 if (!internal_prepare_output_dir(out_dir, out_abs)) {
754 return 1;
755 }
756 mdl_net_iface_t net = {};
757 if (mdl_net_provider_open(opts->net, &opts->policy, &net) != k_ra8_ok) {
759 priv_mdl_app_context()->diagnostic,
760 "mdl: network init failed\n"));
761 return 1;
762 }
763 char ua[k_mdl_ua_max];
764 if (priv_mdl_app_start_session(&net, opts, nullptr, ua, sizeof(ua)) != k_ra8_ok) {
765 mdl_net_destroy(&net);
766 return 1;
767 }
768
769 if (!mdl_session_url_allowed(&priv_mdl_app_context()->session, url, nullptr)) {
770 mdl_net_destroy(&net);
771 return 1;
772 }
773 if (!internal_extract_page_images(url, attr, timeout)) {
774 mdl_net_destroy(&net);
775 return 1;
776 }
777 ra8_err_t output_error = priv_mdl_stream_text(k_ra8_ok, priv_mdl_app_context()->output, "found ");
778 output_error = priv_mdl_stream_u64(output_error,
779 priv_mdl_app_context()->output,
780 priv_mdl_app_context()->images.count);
781 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, " image(s)\n");
782 if (output_error != k_ra8_ok) {
783 mdl_net_destroy(&net);
784 return 1;
785 }
786
787 const size_t fail =
788 internal_download_page_images(url, out_abs, max_imgs, seed, timeout, opts->polite);
789 mdl_net_destroy(&net);
790 return ((fail == 0U) && (priv_mdl_app_context()->io_error == k_ra8_ok)) ? 0 : 1;
791}
792
809RA8_INTERNAL static int
810internal_pack_directory_output(const char* dir, const char* ext, mdl_format_t format)
811{
812 const ra8_err_t error = mdl_export_chapter_ws(&priv_mdl_app_context()->storage,
813 format,
814 dir,
815 dir,
816 &priv_mdl_app_context()->export_ws);
817 if (error != k_ra8_ok) {
818 (void)internal_direct_pack_failure(dir, ext, error);
819 return 1;
820 }
821 ra8_err_t output_error =
822 internal_direct_text3(priv_mdl_app_context()->output, "packed ", dir, " -> ");
823 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, dir);
824 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, "/*.");
825 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, ext);
826 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, "\n");
827 return (output_error == k_ra8_ok) ? 0 : 1;
828}
829
846RA8_INTERNAL static int
847internal_pack_file_output(const char* dir, const char* ext, mdl_format_t format)
848{
849 char out[k_fw_fs_path_cap];
850 const int length = snprintf(out, sizeof(out), "%s.%s", dir, ext);
851 if ((length < 0) || ((size_t)length >= sizeof(out))) {
853 "mdl: output path for '",
854 dir,
855 "' is too long\n");
856 return 1;
857 }
858 const ra8_err_t error = mdl_export_chapter_ws(&priv_mdl_app_context()->storage,
859 format,
860 dir,
861 out,
862 &priv_mdl_app_context()->export_ws);
863 if (error != k_ra8_ok) {
864 (void)internal_direct_pack_failure(dir, ext, error);
865 return 1;
866 }
867 ra8_err_t output_error =
868 internal_direct_text3(priv_mdl_app_context()->output, "packed ", dir, " -> ");
869 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, out);
870 output_error = priv_mdl_stream_text(output_error, priv_mdl_app_context()->output, "\n");
871 return (output_error == k_ra8_ok) ? 0 : 1;
872}
873
874int mdl_app_run_pack(const char* dir, mdl_format_t format)
875{
876 if ((format == k_mdl_format_loose) || (format == k_mdl_format_invalid)) {
877 const ra8_err_t output_error = priv_mdl_stream_text(k_ra8_ok,
878 priv_mdl_app_context()->diagnostic,
879 "mdl: --pack needs a --format "
880 "(cbz|cbt|cbt.gz|epub|jof|rabook)\n");
881 return (output_error == k_ra8_ok) ? 2 : 1;
882 }
883 fw_fs_stat_t source = {};
884 if ((dir == nullptr) || (dir[0] != '/') ||
885 (fw_fs_stat(&priv_mdl_app_context()->storage.fs->names, dir, &source) != k_ra8_ok) ||
886 !source.exists || (source.type != k_fw_fs_node_directory)) {
888 "mdl: cannot resolve '",
889 dir,
890 "'\n");
891 return 1;
892 }
893 const char* ext = mdl_format_ext(format);
894 if (mdl_format_is_dir_output(format)) {
895 /* JOF writes per-page `.jof` siblings into the packed directory itself;
896 * report that directory rather than a container file it never creates. */
897 return internal_pack_directory_output(dir, ext, format);
898 }
899 return internal_pack_file_output(dir, ext, format);
900}
ra8_err_t fw_fs_stat(const fw_fs_namespace_t *names, const char *path, fw_fs_stat_t *out)
Query a path; a miss is success with out->exists == false.
Definition fw_if_fs.c:480
@ k_fw_fs_node_directory
Directory.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
mdl_app_context_t * priv_mdl_app_context(void)
The working set the composition root bound with mdl_app_bind.
Definition mdl_app.c:33
int mdl_app_run_artifact(const char *url, const char *out_dir, uint32_t timeout, const mdl_run_opts_t *opts)
Download, validate and atomically publish one direct artifact.
static ra8_err_t internal_direct_pack_failure(const char *dir, const char *ext, ra8_err_t failure)
Report one pack exporter failure with its exact hexadecimal status.
static ra8_err_t internal_direct_artifact_write(void *context, const uint8_t *bytes, uint32_t length, uint32_t *out_written)
Append one complete response chunk to the portable artifact stage.
static int internal_pack_directory_output(const char *dir, const char *ext, mdl_format_t format)
Package a directory-output format and report its wildcard destination.
static ra8_err_t internal_direct_text3(ra8_io_stream_t *stream, const char *first, const char *second, const char *third)
Append three borrowed direct-action fragments to one stream.
static size_t internal_download_page_image(const char *url, const char *out_dir, uint32_t dmin, uint32_t dmax, uint32_t timeout, mdl_politeness_t *pol, size_t idx)
Gate, space, and download one page-mode image.
static size_t internal_publish_page_image(mdl_fetch_body_t *body, const char *out_dir, size_t idx)
Validate and publish one staged page-mode image.
static bool internal_finish_artifact_fetch(mdl_direct_artifact_sink_t *state, ra8_err_t error, long status)
Finalize or abort one direct artifact transfer.
static ra8_err_t internal_direct_artifact_reset(void *context)
Abort a prior attempt and begin a fresh artifact transaction.
static bool internal_extract_page_images(const char *url, const char *attr, uint32_t timeout)
Fetch one page and populate the shared extracted-image list.
int mdl_app_run_page(const char *url, const char *out_dir, const char *attr, uint32_t max_imgs, uint64_t seed, uint32_t timeout, const mdl_run_opts_t *opts)
Download image resources from one direct page URL.
static int internal_pack_file_output(const char *dir, const char *ext, mdl_format_t format)
Package a file-output format beside its source directory.
static bool internal_prepare_artifact_path(const char *out_dir, const char *leaf, char *final_path)
Prepare the exact final path for one direct artifact.
static bool internal_prepare_output_dir(const char *out_dir, char *out_abs)
Create and canonicalize one direct-action output directory.
int mdl_app_run_pack(const char *dir, mdl_format_t format)
Package an existing image directory without network access.
static size_t internal_download_page_images(const char *url, const char *out_dir, uint32_t max_imgs, uint64_t seed, uint32_t timeout, bool polite)
Download the extracted page images into an output directory.
static uint32_t internal_max_u32(uint32_t a, uint32_t b)
Return the larger of two unsigned values.
static void internal_direct_latch(ra8_err_t error)
Latch the first direct presenter failure for loop cancellation.
static void internal_remove_stale_page_variants(const char *out_dir, size_t page_no, const char *true_ext)
Remove stale page files whose extensions differ from verified bytes.
static bool internal_fetch_artifact(const char *url, const char *final_path, mdl_format_t format, uint32_t timeout, const mdl_run_opts_t *opts, size_t *got)
Fetch one policy-approved artifact into an atomic holding path.
Module-private contract shared by the application-mode translation units.
ra8_err_t priv_mdl_app_start_session(mdl_net_iface_t *net, const mdl_run_opts_t *opts, const char *cfg_contact, char *ua, size_t ua_cap)
Initialize one bounded network-policy session.
@ k_polite_img_min_ms
Polite per-image floor.
@ k_polite_img_max_ms
Polite per-image ceiling.
@ k_ext_bytes
Image-extension buffer.
@ k_page_img_delay_max
page-mode per-image ceiling, ms.
@ k_page_img_delay_min
page-mode per-image floor, ms.
@ k_file_path_bytes
File-path buffer.
@ k_leaf_name_bytes
Composed archive/dir leaf name.
ra8_err_t priv_mdl_app_storage_unlink_regular(mdl_storage_t *storage, const char *path, bool *out_removed)
Remove one regular file while refusing symbolic or special nodes.
ra8_err_t priv_mdl_app_storage_ensure_directory(mdl_storage_t *storage, const char *path)
Ensure one canonical portable path names a real directory.
bool mdl_format_is_dir_output(mdl_format_t fmt)
Whether fmt writes per-page sibling files rather than one container.
Definition mdl_export.c:84
const char * mdl_format_ext(mdl_format_t fmt)
File extension (without dot) for a format, e.g.
Definition mdl_export.c:58
ra8_err_t mdl_export_chapter_ws(mdl_storage_t *storage, mdl_format_t fmt, const char *chapter_dir, const char *out_path, mdl_export_workspace_t *ws)
Package a chapter after auto-loading bounded local metadata.
Definition mdl_export.c:592
ra8_err_t priv_mdl_export_output_abort(mdl_export_output_t *output)
Abort one unpublished export stage, retaining cleanup failure.
ra8_err_t priv_mdl_export_output_commit(mdl_export_output_t *output, mdl_export_workspace_t *workspace, bool *out_published)
Structurally validate and publish one completed export stage.
ra8_err_t priv_mdl_export_output_begin(mdl_export_output_t *output, mdl_storage_t *storage, const char *destination, mdl_format_t format)
Bind a validated publication transaction to one destination.
ra8_err_t priv_mdl_export_output_write(mdl_export_output_t *output, const uint8_t *bytes, uint32_t length)
Append one complete byte span to an active export stage.
Private portable byte-stream contracts for media exporters.
ra8_err_t mdl_extract_images(const char *html, size_t html_len, const char *base_url, const char *prefer_attr, const char *url_contains, mdl_url_list_t *out)
Scan html for <img> image URLs, resolved to absolute form.
void priv_mdl_fetch_reason(ra8_err_t err, long status, char *buf, size_t cap)
Render a human-readable reason for a transfer result and HTTP status.
Definition mdl_fetch.c:130
ra8_err_t priv_mdl_fetch_body_commit(mdl_fetch_body_t *body)
Validate exact size/hash and publish one prepared body.
mdl_net_body_sink_t priv_mdl_fetch_body_sink(mdl_fetch_body_t *body)
Return the network sink view of one body state.
ra8_err_t priv_mdl_fetch_body_prepare(mdl_fetch_body_t *body)
Finish prefix classification and make a nonempty stage commit-ready.
ra8_err_t priv_mdl_fetch_body_init_image(mdl_fetch_body_t *body, mdl_storage_t *storage, const char *target_abs, const char *target_rel)
Initialize a magic-typed image body sink.
ra8_err_t priv_mdl_fetch_body_abort(mdl_fetch_body_t *body)
Abort any private stage owned by a body sink.
Portable single-transaction response-body sink for media fetches.
@ k_mdl_reason_max
Failure-reason string buffer bytes.
mdl_format_t
Artifact format selected by a media-download composition root.
Definition mdl_format.h:35
@ k_mdl_format_invalid
Unrecognized or unsupported selection.
Definition mdl_format.h:45
@ k_mdl_format_loose
Leave loose page images.
Definition mdl_format.h:36
ra8_err_t mdl_net_get_buf(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
GET url fully into a caller buffer (used for HTML pages).
Definition mdl_net.c:78
void mdl_net_destroy(mdl_net_iface_t *net)
Deinitialise a caller-owned network interface.
Definition mdl_net.c:120
ra8_err_t mdl_net_get_body(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, size_t *out_len, mdl_net_resp_t *resp)
GET url and stream its body through a caller-owned sink.
Definition mdl_net.c:97
ra8_err_t mdl_net_provider_open(const mdl_net_provider_t *provider, const mdl_net_policy_t *policy, mdl_net_iface_t *out_net)
Open a transport through an injected provider.
Definition mdl_net.c:64
void mdl_politeness_init(mdl_politeness_t *p, uint64_t seed)
Seed the jitter source, using the real host clock for sleeps.
uint32_t mdl_politeness_wait(mdl_politeness_t *p, uint32_t min_ms, uint32_t max_ms)
Sleep a jittered delay in [min_ms, max_ms] and return it.
bool mdl_path_join(const char *parent, const char *seg, char *out, size_t cap)
Join one safe child segment under a parent directory path.
bool mdl_session_url_allowed(mdl_session_t *session, const char *url, uint32_t *crawl_delay_ms)
Decide whether url may be fetched under robots.txt, and its delay.
@ k_mdl_ua_max
Built User-Agent buffer bytes.
Definition mdl_session.h:27
ra8_err_t priv_mdl_stream_hex(ra8_err_t prior, ra8_io_stream_t *stream, uint32_t value)
Append one hexadecimal error/status value after prior success.
Definition mdl_stream.c:26
ra8_err_t priv_mdl_stream_text(ra8_err_t prior, ra8_io_stream_t *stream, const char *text)
Append text unless an earlier operation already failed.
Definition mdl_stream.c:16
ra8_err_t priv_mdl_stream_u64(ra8_err_t prior, ra8_io_stream_t *stream, uint64_t value)
Append one unsigned integer unless an earlier append failed.
Definition mdl_stream.c:21
void mdl_urlname_last_segment(const char *url, char *out, size_t cap)
Sanitised last non-empty path segment of a URL.
Definition mdl_urlname.c:75
ra8_err_t mdl_verify_file(mdl_storage_t *storage, mdl_format_t format, const char *path, mdl_export_workspace_t *workspace, mdl_verify_report_t *report)
Validate a completed artifact using caller-owned scratch only.
Definition mdl_verify.c:520
bool mdl_format_is_verifiable(mdl_format_t format)
Report whether a format has an in-process structural validator.
Definition mdl_verify.c:131
ra8_err_t mdl_format_from_path(const char *path, mdl_format_t *out_format)
Infer an artifact format from its complete path suffix.
Definition mdl_verify.c:107
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
Result of a portable metadata query.
bool exists
False means a clean lookup miss.
fw_fs_node_type_t type
Kind of node at the path.
bool active
Begin succeeded.
mdl_session_t session
Network policy session.
Definition mdl_app.h:163
ra8_err_t io_error
First presenter sink failure.
Definition mdl_app.h:168
mdl_url_list_t images
Prepared page image URLs.
Definition mdl_app.h:171
mdl_storage_t storage
Injected filesystem facade.
Definition mdl_app.h:165
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
Definition mdl_app.h:167
Transactional body sink for one directly downloaded artifact.
mdl_export_output_t output
Structurally validated output stage.
mdl_storage_t * storage
Exclusive portable storage binding.
const char * destination
Canonical final artifact path.
mdl_format_t format
Exact canonical verifier selection.
One validated staged publication, including random ZIP backfill.
mdl_storage_txn_t writer
Active storage transaction.
Caller-owned state for one retryable streamed fetch.
char actual_abs[PATH_MAX]
Magic-derived final path.
Caller-owned lifecycle and write seam for one response body.
Definition mdl_net.h:162
A network backend handle: an immutable method table plus its state.
Per-request session parameters.
Definition mdl_net.h:43
Per-transfer response metadata surfaced to the politeness governor.
Definition mdl_net.h:120
long status
Finished transfer's HTTP status, 0 if none.
Definition mdl_net.h:121
Deterministic jitter source plus its (optional) injected clock.
Cross-cutting policy and injected transport threaded into every mode.
Definition mdl_app.h:73
mdl_net_policy_t policy
Backend security policy.
Definition mdl_app.h:74
bool polite
True to raise per-host delays.
Definition mdl_app.h:78
const mdl_net_provider_t * net
Injected transport factory.
Definition mdl_app.h:75
const char * user_agent
Full UA header (caller-owned).
Definition mdl_session.h:41
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
fw_fs_transaction_t transaction
Active private filesystem stage.
Definition mdl_storage.h:60
size_t count
Number of valid entries.
Definition mdl_extract.h:31
Caller-allocated byte-stream handle binding a sink to its context.