LCOV - code coverage report
Current view: top level - mhd - mhd_typst.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 3.0 % 297 9
Test Date: 2026-02-16 10:50:18 Functions: 8.3 % 12 1

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file mhd_typst.c
      18              :  * @brief MHD utility functions for PDF generation
      19              :  * @author Christian Grothoff
      20              :  *
      21              :  *
      22              :  */
      23              : #include "taler/platform.h"
      24              : #include "taler/taler_util.h"
      25              : #include "taler/taler_mhd_lib.h"
      26              : #include <microhttpd.h>
      27              : 
      28              : 
      29              : /**
      30              :  * Information about a specific typst invocation.
      31              :  */
      32              : struct TypstStage
      33              : {
      34              :   /**
      35              :    * Name of the FIFO for the typst output.
      36              :    */
      37              :   char *filename;
      38              : 
      39              :   /**
      40              :    * Typst context we are part of.
      41              :    */
      42              :   struct TALER_MHD_TypstContext *tc;
      43              : 
      44              :   /**
      45              :    * Handle to the typst process.
      46              :    */
      47              :   struct GNUNET_OS_Process *proc;
      48              : 
      49              :   /**
      50              :    * Handle to be notified about stage completion.
      51              :    */
      52              :   struct GNUNET_ChildWaitHandle *cwh;
      53              : 
      54              : };
      55              : 
      56              : 
      57              : struct TALER_MHD_TypstContext
      58              : {
      59              : 
      60              :   /**
      61              :    * Directory where we create temporary files (or FIFOs) for the IPC.
      62              :    */
      63              :   char *tmpdir;
      64              : 
      65              :   /**
      66              :    * Array of stages producing PDFs to be combined.
      67              :    */
      68              :   struct TypstStage *stages;
      69              : 
      70              :   /**
      71              :    * Handle for pdftk combining the various PDFs.
      72              :    */
      73              :   struct GNUNET_OS_Process *proc;
      74              : 
      75              :   /**
      76              :    * Handle to wait for @e proc to complete.
      77              :    */
      78              :   struct GNUNET_ChildWaitHandle *cwh;
      79              : 
      80              :   /**
      81              :    * Callback to call on the final result.
      82              :    */
      83              :   TALER_MHD_TypstResultCallback cb;
      84              : 
      85              :   /**
      86              :    * Closure for @e cb
      87              :    */
      88              :   void *cb_cls;
      89              : 
      90              :   /**
      91              :    * Task for async work.
      92              :    */
      93              :   struct GNUNET_SCHEDULER_Task *t;
      94              : 
      95              :   /**
      96              :    * Name of the final file created by pdftk.
      97              :    */
      98              :   char *output_file;
      99              : 
     100              :   /**
     101              :    * Context for fail_async_cb().
     102              :    */
     103              :   char *async_hint;
     104              : 
     105              :   /**
     106              :    * Context for fail_async_cb().
     107              :    */
     108              :   enum TALER_ErrorCode async_ec;
     109              : 
     110              :   /**
     111              :    * Length of the @e stages array.
     112              :    */
     113              :   unsigned int num_stages;
     114              : 
     115              :   /**
     116              :    * Number of still active stages.
     117              :    */
     118              :   unsigned int active_stages;
     119              : 
     120              :   /**
     121              :    * Should the directory be removed when done?
     122              :    */
     123              :   bool remove_on_exit;
     124              : };
     125              : 
     126              : 
     127              : void
     128            0 : TALER_MHD_typst_cancel (struct TALER_MHD_TypstContext *tc)
     129              : {
     130            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     131              :               "Cleaning up TypstContext\n");
     132            0 :   if (NULL != tc->t)
     133              :   {
     134            0 :     GNUNET_SCHEDULER_cancel (tc->t);
     135            0 :     tc->t = NULL;
     136              :   }
     137            0 :   for (unsigned int i = 0; i<tc->num_stages; i++)
     138              :   {
     139            0 :     struct TypstStage *stage = &tc->stages[i];
     140              : 
     141            0 :     if (NULL != stage->cwh)
     142              :     {
     143            0 :       GNUNET_wait_child_cancel (stage->cwh);
     144            0 :       stage->cwh = NULL;
     145              :     }
     146            0 :     if (NULL != stage->proc)
     147              :     {
     148            0 :       GNUNET_break (0 ==
     149              :                     GNUNET_OS_process_kill (stage->proc,
     150              :                                             SIGKILL));
     151            0 :       GNUNET_OS_process_destroy (stage->proc);
     152            0 :       stage->proc = NULL;
     153              :     }
     154            0 :     GNUNET_free (stage->filename);
     155              :   }
     156            0 :   GNUNET_free (tc->stages);
     157            0 :   if (NULL != tc->cwh)
     158              :   {
     159            0 :     GNUNET_wait_child_cancel (tc->cwh);
     160            0 :     tc->cwh = NULL;
     161              :   }
     162            0 :   if (NULL != tc->proc)
     163              :   {
     164            0 :     GNUNET_break (0 ==
     165              :                   GNUNET_OS_process_kill (tc->proc,
     166              :                                           SIGKILL));
     167            0 :     GNUNET_OS_process_destroy (tc->proc);
     168              :   }
     169            0 :   GNUNET_free (tc->output_file);
     170            0 :   if (NULL != tc->tmpdir)
     171              :   {
     172            0 :     if (tc->remove_on_exit)
     173            0 :       GNUNET_DISK_directory_remove (tc->tmpdir);
     174            0 :     GNUNET_free (tc->tmpdir);
     175              :   }
     176            0 :   GNUNET_free (tc);
     177            0 : }
     178              : 
     179              : 
     180              : /**
     181              :  * Create file in @a tmpdir with one of the PDF inputs.
     182              :  *
     183              :  * @param[out] stage initialized stage data
     184              :  * @param tmpdir where to place temporary files
     185              :  * @param data input JSON with PDF data
     186              :  * @return true on success
     187              :  */
     188              : static bool
     189            0 : inline_pdf_stage (struct TypstStage *stage,
     190              :                   const char *tmpdir,
     191              :                   const json_t *data)
     192              : {
     193            0 :   const char *str = json_string_value (data);
     194              :   char *fn;
     195              :   size_t n;
     196              :   void *b;
     197              :   int fd;
     198              : 
     199            0 :   if (NULL == str)
     200              :   {
     201            0 :     GNUNET_break (0);
     202            0 :     return false;
     203              :   }
     204            0 :   b = NULL;
     205            0 :   n = GNUNET_STRINGS_base64_decode (str,
     206              :                                     strlen (str),
     207              :                                     &b);
     208            0 :   if (NULL == b)
     209              :   {
     210            0 :     GNUNET_break (0);
     211            0 :     return false;
     212              :   }
     213            0 :   GNUNET_asprintf (&fn,
     214              :                    "%s/external-",
     215              :                    tmpdir);
     216            0 :   stage->filename = GNUNET_DISK_mktemp (fn);
     217            0 :   if (NULL == stage->filename)
     218              :   {
     219            0 :     GNUNET_break (0);
     220            0 :     GNUNET_free (b);
     221            0 :     GNUNET_free (fn);
     222            0 :     return false;
     223              :   }
     224            0 :   GNUNET_free (fn);
     225            0 :   fd = open (stage->filename,
     226              :              O_WRONLY | O_TRUNC,
     227              :              S_IRUSR | S_IWUSR);
     228            0 :   if (-1 == fd)
     229              :   {
     230            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     231              :                               "open",
     232              :                               stage->filename);
     233            0 :     GNUNET_free (b);
     234            0 :     GNUNET_free (stage->filename);
     235            0 :     return false;
     236              :   }
     237              : 
     238              :   {
     239            0 :     size_t off = 0;
     240              : 
     241            0 :     while (off < n)
     242              :     {
     243              :       ssize_t r;
     244              : 
     245            0 :       r = write (fd,
     246            0 :                  b + off,
     247              :                  n - off);
     248            0 :       if (-1 == r)
     249              :       {
     250            0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     251              :                                   "write",
     252              :                                   stage->filename);
     253            0 :         GNUNET_break (0 == close (fd));
     254            0 :         GNUNET_free (b);
     255            0 :         GNUNET_free (stage->filename);
     256            0 :         return false;
     257              :       }
     258            0 :       off += r;
     259              :     }
     260              :   }
     261            0 :   GNUNET_break (0 == close (fd));
     262            0 :   return true;
     263              : }
     264              : 
     265              : 
     266              : /**
     267              :  * Generate a response for @a tc indicating an error of type @a ec.
     268              :  *
     269              :  * @param[in,out] tc context to fail
     270              :  * @param ec error code to return
     271              :  * @param hint hint text to return
     272              :  */
     273              : static void
     274            0 : typst_context_fail (struct TALER_MHD_TypstContext *tc,
     275              :                     enum TALER_ErrorCode ec,
     276              :                     const char *hint)
     277              : {
     278            0 :   struct TALER_MHD_TypstResponse resp = {
     279              :     .ec = ec,
     280              :     .details.hint = hint
     281              :   };
     282              : 
     283            0 :   if (NULL != tc->cb)
     284              :   {
     285            0 :     tc->cb (tc->cb_cls,
     286              :             &resp);
     287            0 :     tc->cb = NULL;
     288              :   }
     289            0 : }
     290              : 
     291              : 
     292              : /**
     293              :  * Helper task for typst_context_fail_async().
     294              :  *
     295              :  * @param cls a `struct TALER_MHD_TypstContext`
     296              :  */
     297              : static void
     298            0 : fail_async_cb (void *cls)
     299              : {
     300            0 :   struct TALER_MHD_TypstContext *tc = cls;
     301              : 
     302            0 :   tc->t = NULL;
     303            0 :   typst_context_fail (tc,
     304              :                       tc->async_ec,
     305            0 :                       tc->async_hint);
     306            0 :   GNUNET_free (tc->async_hint);
     307            0 :   TALER_MHD_typst_cancel (tc);
     308            0 : }
     309              : 
     310              : 
     311              : /**
     312              :  * Generate a response for @a tc indicating an error of type @a ec.
     313              :  *
     314              :  * @param[in,out] tc context to fail
     315              :  * @param ec error code to return
     316              :  * @param hint hint text to return
     317              :  */
     318              : static void
     319            0 : typst_context_fail_async (struct TALER_MHD_TypstContext *tc,
     320              :                           enum TALER_ErrorCode ec,
     321              :                           const char *hint)
     322              : {
     323            0 :   tc->async_ec = ec;
     324            0 :   tc->async_hint = (NULL == hint) ? NULL : GNUNET_strdup (hint);
     325            0 :   tc->t = GNUNET_SCHEDULER_add_now (&fail_async_cb,
     326              :                                     tc);
     327            0 : }
     328              : 
     329              : 
     330              : /**
     331              :  * Called when the pdftk helper exited.
     332              :  *
     333              :  * @param cls our `struct TALER_MHD_TypstContext *`
     334              :  * @param type type of the process
     335              :  * @param exit_code status code of the process
     336              :  */
     337              : static void
     338            0 : pdftk_done_cb (void *cls,
     339              :                enum GNUNET_OS_ProcessStatusType type,
     340              :                long unsigned int exit_code)
     341              : {
     342            0 :   struct TALER_MHD_TypstContext *tc = cls;
     343              : 
     344            0 :   tc->cwh = NULL;
     345            0 :   GNUNET_OS_process_destroy (tc->proc);
     346            0 :   tc->proc = NULL;
     347            0 :   switch (type)
     348              :   {
     349            0 :   case GNUNET_OS_PROCESS_UNKNOWN:
     350            0 :     GNUNET_assert (0);
     351              :     return;
     352            0 :   case GNUNET_OS_PROCESS_RUNNING:
     353              :     /* we should not get this notification */
     354            0 :     GNUNET_break (0);
     355            0 :     return;
     356            0 :   case GNUNET_OS_PROCESS_STOPPED:
     357              :     /* Someone is SIGSTOPing our helper!? */
     358            0 :     GNUNET_break (0);
     359            0 :     return;
     360            0 :   case GNUNET_OS_PROCESS_EXITED:
     361            0 :     if (0 != exit_code)
     362              :     {
     363            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     364              :                   "pdftk exited with status %d\n",
     365              :                   (int) exit_code);
     366            0 :       typst_context_fail (tc,
     367              :                           TALER_EC_EXCHANGE_GENERIC_PDFTK_FAILURE,
     368              :                           "pdftk failed");
     369              :     }
     370              :     else
     371              :     {
     372            0 :       struct TALER_MHD_TypstResponse resp = {
     373              :         .ec = TALER_EC_NONE,
     374            0 :         .details.filename = tc->output_file,
     375              :       };
     376              : 
     377            0 :       GNUNET_assert (NULL != tc->cb);
     378            0 :       tc->cb (tc->cb_cls,
     379              :               &resp);
     380            0 :       tc->cb = NULL;
     381              :     }
     382            0 :     break;
     383            0 :   case GNUNET_OS_PROCESS_SIGNALED:
     384            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     385              :                 "pdftk died with signal %d\n",
     386              :                 (int) exit_code);
     387            0 :     typst_context_fail (tc,
     388              :                         TALER_EC_EXCHANGE_GENERIC_PDFTK_CRASH,
     389              :                         "pdftk killed by signal");
     390            0 :     break;
     391              :   }
     392            0 :   TALER_MHD_typst_cancel (tc);
     393              : }
     394              : 
     395              : 
     396              : /**
     397              :  * Function called once all of the individual stages are done.
     398              :  * Triggers the pdftk run for @a tc.
     399              :  *
     400              :  * @param[in,out] cls a `struct TALER_MHD_TypstContext *` context to run pdftk for
     401              :  */
     402              : static void
     403            0 : complete_response (void *cls)
     404            0 : {
     405            0 :   struct TALER_MHD_TypstContext *tc = cls;
     406            0 :   const char *argv[tc->num_stages + 5];
     407              : 
     408            0 :   tc->t = NULL;
     409            0 :   argv[0] = "pdftk";
     410            0 :   for (unsigned int i = 0; i<tc->num_stages; i++)
     411            0 :     argv[i + 1] = tc->stages[i].filename;
     412            0 :   argv[tc->num_stages + 1] = "cat";
     413            0 :   argv[tc->num_stages + 2] = "output";
     414            0 :   argv[tc->num_stages + 3] = tc->output_file;
     415            0 :   argv[tc->num_stages + 4] = NULL;
     416            0 :   tc->proc = GNUNET_OS_start_process_vap (
     417              :     GNUNET_OS_INHERIT_STD_ERR,
     418              :     NULL,
     419              :     NULL,
     420              :     NULL,
     421              :     argv[0],
     422              :     (char **) argv);
     423            0 :   if (NULL == tc->proc)
     424              :   {
     425            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     426              :                          "fork");
     427            0 :     TALER_MHD_typst_cancel (tc);
     428            0 :     return;
     429              :   }
     430            0 :   tc->cwh = GNUNET_wait_child (tc->proc,
     431              :                                &pdftk_done_cb,
     432              :                                tc);
     433            0 :   GNUNET_assert (NULL != tc->cwh);
     434              : }
     435              : 
     436              : 
     437              : /**
     438              :  * Cancel typst. Wrapper task to do so asynchronously.
     439              :  *
     440              :  * @param[in] cls a `struct TALER_MHD_TypstContext`
     441              :  */
     442              : static void
     443            0 : cancel_async (void *cls)
     444              : {
     445            0 :   struct TALER_MHD_TypstContext *tc = cls;
     446              : 
     447            0 :   tc->t = NULL;
     448            0 :   TALER_MHD_typst_cancel (tc);
     449            0 : }
     450              : 
     451              : 
     452              : /**
     453              :  * Called when a typst helper exited.
     454              :  *
     455              :  * @param cls our `struct TypstStage *`
     456              :  * @param type type of the process
     457              :  * @param exit_code status code of the process
     458              :  */
     459              : static void
     460            0 : typst_done_cb (void *cls,
     461              :                enum GNUNET_OS_ProcessStatusType type,
     462              :                long unsigned int exit_code)
     463              : {
     464            0 :   struct TypstStage *stage = cls;
     465            0 :   struct TALER_MHD_TypstContext *tc = stage->tc;
     466              : 
     467            0 :   stage->cwh = NULL;
     468            0 :   GNUNET_OS_process_destroy (stage->proc);
     469            0 :   stage->proc = NULL;
     470            0 :   switch (type)
     471              :   {
     472            0 :   case GNUNET_OS_PROCESS_UNKNOWN:
     473            0 :     GNUNET_assert (0);
     474              :     return;
     475            0 :   case GNUNET_OS_PROCESS_RUNNING:
     476              :     /* we should not get this notification */
     477            0 :     GNUNET_break (0);
     478            0 :     return;
     479            0 :   case GNUNET_OS_PROCESS_STOPPED:
     480              :     /* Someone is SIGSTOPing our helper!? */
     481            0 :     GNUNET_break (0);
     482            0 :     return;
     483            0 :   case GNUNET_OS_PROCESS_EXITED:
     484            0 :     if (0 != exit_code)
     485              :     {
     486              :       char err[128];
     487              : 
     488            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     489              :                   "typst exited with status %d\n",
     490              :                   (int) exit_code);
     491            0 :       GNUNET_snprintf (err,
     492              :                        sizeof (err),
     493              :                        "Typst exited with status %d",
     494              :                        (int) exit_code);
     495            0 :       typst_context_fail (tc,
     496              :                           TALER_EC_EXCHANGE_GENERIC_TYPST_TEMPLATE_FAILURE,
     497              :                           err);
     498            0 :       GNUNET_assert (NULL == tc->t);
     499            0 :       tc->t = GNUNET_SCHEDULER_add_now (&cancel_async,
     500              :                                         tc);
     501            0 :       return;
     502              :     }
     503            0 :     break;
     504            0 :   case GNUNET_OS_PROCESS_SIGNALED:
     505              :     {
     506              :       char err[128];
     507              : 
     508            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     509              :                   "typst died with signal %d\n",
     510              :                   (int) exit_code);
     511            0 :       GNUNET_snprintf (err,
     512              :                        sizeof (err),
     513              :                        "Typst died with signal %d",
     514              :                        (int) exit_code);
     515            0 :       typst_context_fail (tc,
     516              :                           TALER_EC_EXCHANGE_GENERIC_TYPST_CRASH,
     517              :                           err);
     518            0 :       GNUNET_assert (NULL == tc->t);
     519            0 :       tc->t = GNUNET_SCHEDULER_add_now (&cancel_async,
     520              :                                         tc);
     521            0 :       return;
     522              :     }
     523              :     break;
     524              :   }
     525            0 :   tc->active_stages--;
     526            0 :   if (NULL != stage->proc)
     527              :   {
     528            0 :     GNUNET_OS_process_destroy (stage->proc);
     529            0 :     stage->proc = NULL;
     530              :   }
     531            0 :   if (0 != tc->active_stages)
     532            0 :     return;
     533            0 :   GNUNET_assert (NULL == tc->t);
     534            0 :   tc->t = GNUNET_SCHEDULER_add_now (&complete_response,
     535              :                                     tc);
     536              : }
     537              : 
     538              : 
     539              : /**
     540              :  * Setup typst stage to produce one of the PDF inputs.
     541              :  *
     542              :  * @param[out] stage initialized stage data
     543              :  * @param i index of the stage
     544              :  * @param tmpdir where to place temporary files
     545              :  * @param template_path where to find templates
     546              :  * @param doc input document specification
     547              :  * @return true on success
     548              :  */
     549              : static bool
     550            0 : setup_stage (struct TypstStage *stage,
     551              :              unsigned int i,
     552              :              const char *tmpdir,
     553              :              const char *template_path,
     554              :              const struct TALER_MHD_TypstDocument *doc)
     555              : {
     556              :   char *input;
     557              : 
     558            0 :   if (NULL == doc->form_name)
     559              :   {
     560            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     561              :                 "Stage %u: Dumping inlined PDF attachment\n",
     562              :                 i);
     563            0 :     return inline_pdf_stage (stage,
     564              :                              tmpdir,
     565            0 :                              doc->data);
     566              :   }
     567              : 
     568            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     569              :               "Stage %u: Handling form %s\n",
     570              :               i,
     571              :               doc->form_name);
     572              : 
     573              :   /* Setup inputs */
     574              :   {
     575              :     char *dirname;
     576              : 
     577            0 :     GNUNET_asprintf (&dirname,
     578              :                      "%s/%u/",
     579              :                      tmpdir,
     580              :                      i);
     581            0 :     if (GNUNET_OK !=
     582            0 :         GNUNET_DISK_directory_create (dirname))
     583              :     {
     584            0 :       GNUNET_free (dirname);
     585            0 :       return false;
     586              :     }
     587            0 :     GNUNET_free (dirname);
     588              :   }
     589              : 
     590              :   /* Setup data input */
     591              :   {
     592              :     char *jfn;
     593              : 
     594            0 :     GNUNET_asprintf (&jfn,
     595              :                      "%s/%u/input.json",
     596              :                      tmpdir,
     597              :                      i);
     598            0 :     if (0 !=
     599            0 :         json_dump_file (doc->data,
     600              :                         jfn,
     601              :                         JSON_INDENT (2)))
     602              :     {
     603            0 :       GNUNET_break (0);
     604            0 :       GNUNET_free (jfn);
     605            0 :       return false;
     606              :     }
     607            0 :     GNUNET_free (jfn);
     608              :   }
     609              : 
     610              :   /* setup output file name */
     611            0 :   GNUNET_asprintf (&stage->filename,
     612              :                    "%s/%u/input.pdf",
     613              :                    tmpdir,
     614              :                    i);
     615              : 
     616              :   /* setup main input Typst file */
     617              :   {
     618              :     char *intyp;
     619              :     char *template_fn;
     620              : 
     621            0 :     GNUNET_asprintf (&template_fn,
     622              :                      "%s%s.typ",
     623              :                      template_path,
     624            0 :                      doc->form_name);
     625            0 :     if (GNUNET_YES !=
     626            0 :         GNUNET_DISK_file_test_read (template_fn))
     627              :     {
     628            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     629              :                                 "access",
     630              :                                 template_fn);
     631            0 :       GNUNET_free (template_fn);
     632            0 :       return false;
     633              :     }
     634            0 :     GNUNET_asprintf (&intyp,
     635              :                      "#import \"%s\": form\n"
     636              :                      "#form(json(\"%s/%u/input.json\"))\n",
     637              :                      template_fn,
     638              :                      tmpdir,
     639              :                      i);
     640            0 :     GNUNET_asprintf (&input,
     641              :                      "%s/%u/input.typ",
     642              :                      tmpdir,
     643              :                      i);
     644            0 :     if (GNUNET_OK !=
     645            0 :         GNUNET_DISK_fn_write (input,
     646              :                               intyp,
     647              :                               strlen (intyp),
     648              :                               GNUNET_DISK_PERM_USER_READ))
     649              :     {
     650            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     651              :                                 "write",
     652              :                                 input);
     653            0 :       GNUNET_free (input);
     654            0 :       GNUNET_free (intyp);
     655            0 :       GNUNET_free (template_fn);
     656            0 :       return false;
     657              :     }
     658            0 :     GNUNET_free (template_fn);
     659            0 :     GNUNET_free (intyp);
     660              :   }
     661              : 
     662              :   /* now setup typst invocation */
     663              :   {
     664              :     const char *argv[6];
     665              : 
     666            0 :     argv[0] = "typst";
     667            0 :     argv[1] = "compile";
     668              :     /* This deliberately breaks the typst sandbox. Why? Because
     669              :        they suck and do not support multiple roots, but we have
     670              :        dynamic data in /tmp and resources outside of /tmp and
     671              :        copying all the time is also bad. Typst should really
     672              :        support multiple roots. */
     673            0 :     argv[2] = "--root";
     674            0 :     argv[3] = "/";
     675            0 :     argv[4] = input;
     676            0 :     argv[5] = NULL;
     677            0 :     stage->proc = GNUNET_OS_start_process_vap (
     678              :       GNUNET_OS_INHERIT_STD_ERR,
     679              :       NULL,
     680              :       NULL,
     681              :       NULL,
     682              :       "typst",
     683              :       (char **) argv);
     684            0 :     if (NULL == stage->proc)
     685              :     {
     686            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     687              :                            "fork");
     688            0 :       GNUNET_free (input);
     689            0 :       return false;
     690              :     }
     691            0 :     GNUNET_free (input);
     692            0 :     stage->tc->active_stages++;
     693            0 :     stage->cwh = GNUNET_wait_child (stage->proc,
     694              :                                     &typst_done_cb,
     695              :                                     stage);
     696            0 :     GNUNET_assert (NULL != stage->cwh);
     697              :   }
     698            0 :   return true;
     699              : }
     700              : 
     701              : 
     702              : struct TALER_MHD_TypstContext *
     703            1 : TALER_MHD_typst (
     704              :   const struct GNUNET_CONFIGURATION_Handle *cfg,
     705              :   bool remove_on_exit,
     706              :   const char *cfg_section_name,
     707              :   unsigned int num_documents,
     708              :   const struct TALER_MHD_TypstDocument docs[static num_documents],
     709              :   TALER_MHD_TypstResultCallback cb,
     710              :   void *cb_cls)
     711            1 : {
     712              :   static enum GNUNET_GenericReturnValue once = GNUNET_NO;
     713              :   struct TALER_MHD_TypstContext *tc;
     714              : 
     715            1 :   switch (once)
     716              :   {
     717            0 :   case GNUNET_OK:
     718            0 :     break;
     719            1 :   case GNUNET_NO:
     720            1 :     if (GNUNET_SYSERR ==
     721            1 :         GNUNET_OS_check_helper_binary ("typst",
     722              :                                        false,
     723              :                                        NULL))
     724              :     {
     725            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     726              :                   "`typst' command not found\n");
     727            1 :       once = GNUNET_SYSERR;
     728            1 :       return NULL;
     729              :     }
     730            0 :     if (GNUNET_SYSERR ==
     731            0 :         GNUNET_OS_check_helper_binary ("pdftk",
     732              :                                        false,
     733              :                                        NULL))
     734              :     {
     735            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     736              :                   "`pdftk' command not found\n");
     737            0 :       once = GNUNET_SYSERR;
     738            0 :       return NULL;
     739              :     }
     740            0 :     once = GNUNET_OK;
     741            0 :     break;
     742            0 :   case GNUNET_SYSERR:
     743            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     744              :                 "PDF generation initialization failed before, not even trying again\n");
     745            0 :     return NULL;
     746              :   }
     747            0 :   tc = GNUNET_new (struct TALER_MHD_TypstContext);
     748            0 :   tc->tmpdir = GNUNET_strdup ("/tmp/taler-typst-XXXXXX");
     749            0 :   tc->remove_on_exit = remove_on_exit;
     750            0 :   tc->cb = cb;
     751            0 :   tc->cb_cls = cb_cls;
     752            0 :   if (NULL == mkdtemp (tc->tmpdir))
     753              :   {
     754            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     755              :                               "mkdtemp",
     756              :                               tc->tmpdir);
     757            0 :     GNUNET_free (tc->tmpdir);
     758            0 :     TALER_MHD_typst_cancel (tc);
     759            0 :     return NULL;
     760              :   }
     761            0 :   GNUNET_asprintf (&tc->output_file,
     762              :                    "%s/final.pdf",
     763              :                    tc->tmpdir);
     764              : 
     765              :   /* setup typst stages */
     766              :   {
     767              :     char *template_path;
     768              : 
     769            0 :     if (GNUNET_OK !=
     770            0 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     771              :                                                  cfg_section_name,
     772              :                                                  "TYPST_TEMPLATES",
     773              :                                                  &template_path))
     774              :     {
     775            0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     776              :                                  cfg_section_name,
     777              :                                  "TYPST_TEMPLATES");
     778            0 :       TALER_MHD_typst_cancel (tc);
     779            0 :       return NULL;
     780              :     }
     781            0 :     tc->stages = GNUNET_new_array (num_documents,
     782              :                                    struct TypstStage);
     783            0 :     tc->num_stages = num_documents;
     784            0 :     for (unsigned int i = 0; i<num_documents; i++)
     785              :     {
     786            0 :       tc->stages[i].tc = tc;
     787            0 :       if (! setup_stage (&tc->stages[i],
     788              :                          i,
     789            0 :                          tc->tmpdir,
     790              :                          template_path,
     791            0 :                          &docs[i]))
     792              :       {
     793              :         char err[128];
     794              : 
     795            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     796              :                     "Typst setup failed on stage %u\n",
     797              :                     i);
     798            0 :         GNUNET_snprintf (err,
     799              :                          sizeof (err),
     800              :                          "Typst setup failed on stage %u",
     801              :                          i);
     802            0 :         typst_context_fail_async (tc,
     803              :                                   TALER_EC_EXCHANGE_GENERIC_TYPST_TEMPLATE_FAILURE,
     804              :                                   err);
     805            0 :         return tc;
     806              :       }
     807              :     }
     808            0 :     GNUNET_free (template_path);
     809              :   }
     810            0 :   if (0 == tc->active_stages)
     811              :   {
     812            0 :     tc->t = GNUNET_SCHEDULER_add_now (&complete_response,
     813              :                                       tc);
     814              :   }
     815            0 :   return tc;
     816              : }
     817              : 
     818              : 
     819              : struct MHD_Response *
     820            0 : TALER_MHD_response_from_pdf_file (const char *filename)
     821              : {
     822              :   struct MHD_Response *resp;
     823              :   struct stat s;
     824              :   int fd;
     825              : 
     826            0 :   fd = open (filename,
     827              :              O_RDONLY);
     828            0 :   if (-1 == fd)
     829              :   {
     830            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     831              :                               "open",
     832              :                               filename);
     833            0 :     return NULL;
     834              :   }
     835            0 :   if (0 !=
     836            0 :       fstat (fd,
     837              :              &s))
     838              :   {
     839            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     840              :                               "fstat",
     841              :                               filename);
     842            0 :     GNUNET_assert (0 == close (fd));
     843            0 :     return NULL;
     844              :   }
     845            0 :   resp = MHD_create_response_from_fd (s.st_size,
     846              :                                       fd);
     847            0 :   TALER_MHD_add_global_headers (resp,
     848              :                                 false);
     849            0 :   GNUNET_break (MHD_YES ==
     850              :                 MHD_add_response_header (resp,
     851              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     852              :                                          "application/pdf"));
     853            0 :   return resp;
     854              : }
        

Generated by: LCOV version 2.0-1