<?php if (!defined('PmWiki')) exit();
/* 
  Copyright 2019-2026 Petko Yotov www.pmwiki.org/petko
  This file is part of PmWiki; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published
  by the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.  See pmwiki.php for full details.

  This helper function combines a multipart message with plain text,
  wiki markup (converted to html), html, embedded pictures and attached files.
  It returns the combined message and an additional multipart/mixed header.
  Based on Cookbook:SMTPMail by Petko Yotov

  Not included by default, enable it with such a line:
  include_once("$FarmD/scripts/multipart.php");
*/


function PmMultipartMail($parts, $pn=null) {
  global $Charset, $UploadExts, $pagename, $PmMultipartMailCSS, $LinkFunctions, 
    $PmMultipartMarkupTemplate, $PmMMCids, $PmMMCidMap, $PmMMCidDirs, $PmMMCTE;
  
  if (is_null($pn)) $pn = $pagename;
  $message = '';
  
  SDV($PmMMCidDirs, 'pub|uploads');
  $encodings = [
    'base64' => 'base64_encode',
    'quoted-printable' => 'quoted_printable_encode',
  ];
  
  Markup('cid:img', '<img', "/\\b(cid:((?:$PmMMCidDirs)\\/[-\\\\a-z0-9._\\/]+)\\.(?:gif|jpe?g|png))(?:\"([^\"]*)\")?/i", 'mu_cid_img');
  
  
  $boundary = "PM-MULTIPART-MIXED-BOUNDARY";
  
  $lf = $LinkFunctions;
  $LinkFunctions['mailto:'] = 'LinkIMap'; # if custom mailto: function
  
  $headers = [ 
    'MIME-Version' => '1.0',
    'Content-Type' => "multipart/mixed; boundary=\"$boundary\"",
  ];
  
  $parts = (array)$parts;
  while (!empty($parts)) {
    $content = array_shift($parts);
    $j = 'text'; $fname = $cid = $ct = '';
    
    if (preg_match('/^(cid|file|markup|html|content|contentasfile):(.*)$/s', $content, $m)) {
      $j = $m[1]; $content = $m[2];
    }
    if ($j=='contentasfile') { # used for example for CSV data
      list($fname, $content) = explode("\n", $content, 2);
    }
    elseif ($j=='cid'||$j=='file') {
      if (!file_exists($m[2])) continue;
      $fname = preg_replace('!/+!s', '-', $m[2]);
      $content = file_get_contents($m[2]);
    }
    elseif ($j=='markup') {
      $PmMMCids = [];
      $tpl = IsEnabled($PmMultipartMarkupTemplate, '');
      if (strpos($tpl, '{$$content}') !== false)
        $content = str_replace('{$$content}', $content, $tpl);
      $content = MarkupToHTML($pn, $content);
      if(is_array($PmMMCidMap) && $PmMMCidMap) {
        $content = preg_replace_callback('/(<img[^>]* src=([\'"]))(.*?)(\\2.*?>)/si', 'PmMMEmbedded', $content);
      }
      foreach($PmMMCids as $cid=>$ignore) {
        $parts[] = $cid;
      }
    }
    
    if ($j=='text') {
      $ct = "text/plain; charset=$Charset";
    }
    elseif ($j=='markup'||$j=='html') {
      $ct = "text/html; charset=$Charset";
      
      if (!preg_match('!<html.*?>!si', $content)) {
        $styles = empty($PmMultipartMailCSS) ? '': implode("\n", (array)$PmMultipartMailCSS);
        
        $content = "<!doctype html><html><head><meta charset=\"$Charset\">
<style>.vspace{margin-top:1.5rem;} .indent{margin-left:40px;} .right{text-align:right;}$styles</style>
</head><body>$content</body></html> ";
      }
    }
    else {
      $ext = strtolower(preg_replace('!^.*\\.!', '', $fname));
      if (isset($UploadExts[$ext])) $ct = $UploadExts[$ext];
      else $ct = 'application/octet-stream';
    }
    if ($fname) $ct .= "; name=\"$fname\"";
    
    $message .= "--$boundary\n";
    $message .= "Content-Type: $ct\n";
    if ($j=='cid') {
      $message .= "Content-ID: <$fname>\n";
      $message .= "X-Attachment-Id: $fname\n";
    }
       
    if ($j=='markup'||$j=='text'||$j=='html') $cd = '';
    elseif ($j=='cid') $cd = 'inline';
    else $cd = "attachment";
    
    if ($cd && $fname) $cd .= "; filename=\"$fname\"";
    
    if ($cd) $message .= "Content-Disposition: $cd\n";
    
    if ($j!='text' && $j!='markup' && $j!='html') {
      $content = chunk_split(base64_encode($content), 76, "\n");
      $message .= "Content-Transfer-Encoding: base64\n";
    }
    if($j=='markup' || $j=='html') {
      $ctefn = $encodings[$PmMMCTE]??false;
      if($ctefn) {
        $message .= "Content-Transfer-Encoding: $PmMMCTE\n";
        $content = $ctefn($content);
      }
    }
    $message .= "\n$content\n\n";
  }
  
  if(count($parts)==1 && substr($ct, 0, 5) == 'text/') {
    $headers['Content-Type'] = $ct;
    $message = $content;
  }
  else {
    $message .= "--$boundary--\n";
  }
  
  $message = str_replace("\n", "\r\n", $message);
  
  $LinkFunctions = $lf;
  return [$message, $headers];
}

function PmMMEmbedded($m) {
  global $PmMMCids, $PmMMCidMap;
  list($all, $start, $q, $url, $end) = $m;
  if(!preg_match('/\\.(png|gif|jpg|webp)$/i', $url)) return $all;
  $path = PPRA($PmMMCidMap, $url);
  $path = preg_replace('!\\.+/+!', '', $path); # prevent ../../
  if(strpos($path, ':')!==false) return $all;
  if(!file_exists($path)) return $all;
  $cid = preg_replace('!/+!', '-', $path);
  $PmMMCids["cid:$path"] = 1;
  return "{$start}cid:$cid$end";
}

function mu_cid_img($m) {
  global $PmMMCids;
  $part = preg_replace('!\\.+/+!', '', $m[1]); # prevent ../../
  $url = preg_replace('!/+!', '-', $part);
  $PmMMCids[$part] = 1;
  $alt = isset($m[3]) ? $m[3] : $m[2];
  return Keep("<img src='$url' alt='$alt' title='$alt'>", 'L');
}

