<?php
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
if(!defined('DOKU_MEDIA')) define('DOKU_MEDIA',DOKU_INC.'data/media/');
define ('BROKEN_IMAGE', DOKU_BASE . 'lib/plugins/ckgedit/fckeditor/userfiles/blink.jpg?nolink&33x34');
require_once(DOKU_PLUGIN.'action.php');
if(!defined('FCK_ACTION_SUBDIR')) define('FCK_ACTION_SUBDIR', realpath(dirname(__FILE__)) . '/');
/**
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 */

class action_plugin_ckgedit_save extends DokuWiki_Action_Plugin {
     public $helper = false;
    function register(Doku_Event_Handler $controller) {
  
        $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'ckgedit_save_preprocess');
    }

    function ckgedit_save_preprocess(Doku_Event $event){
        global $ACT,$INPUT;
        $this->helper = $this->loadhelper('ckgedit');
        if (!isset($_REQUEST['ckgedit']) || ! is_array($ACT) || !(isset($ACT['save']) || isset($ACT['preview']))) return;
         if (isset($_REQUEST["fontdel"]) ) {
             msg($this->getLang("fontdel"),1);           
         }
         if (isset($_REQUEST["formatdel"]) ) {
             msg($this->getLang("formatdel"),1);           
         }         
        if (isset($_REQUEST["linkintbl"]) ) {             
             msg($this->getLang("list_in_table"),1);
         }         
         $img_size = $INPUT->int('broken_image');
         if($img_size) msg($this->getLang('broken_image') . $img_size/1000000 . 'M' ); 
      
       
        global $TEXT, $conf;
             
        if (!$TEXT) return;
        $preserve_enc = $this->getConf('preserve_enc');        
        $deaccent = $conf['deaccent'] == 0 ? false : true;
        $TEXT = $_REQUEST['fck_wikitext'];
        
        if(!preg_match('/^\s+(\-|\*)/',$TEXT)) {    
              $TEXT = trim($TEXT);
        }

   
  $TEXT = preg_replace_callback(
    '|\{\{data:(.*?);base64|ms',
      function($matches) {
         if(!preg_match("/image/",$matches[1])) {
          return "{{data:image/jpeg;base64";
         }
          return $matches[0];         
        },$TEXT);
      
    if(strpos($TEXT,'data:image') !== false) {
        $TEXT = preg_replace_callback(
             '|\{\{(\s*)data:image\/(\w+;base64,\s*)(.*?)\?nolink&(\s*)\}\}|ms',
             function($matches) {
                list($ext,$base) = explode(";",$matches[2]);
                if($ext == "jpeg" || $ext == "tiff") $ext = "jpg";                    
                 if(function_exists("imagecreatefromstring") && !imagecreatefromstring (base64_decode($matches[3]))) {
                     msg("Clipboard paste: invalid $ext image format");
                     return "{{" . BROKEN_IMAGE .  "}}";
                 }                 
                  global $INFO,$conf,$ID;                 
                  $fn = $this->get_imgpaste_fname($ext);               
                  if(!$fn) {                  
                  $ns = getNS($INFO["id"]);                                    
                  $ns = trim($ns);
                  if(!empty($ns)) {                     
                      $ns = ":$ns:";
                       $dir = str_replace(":","/",$ns);                     
                  }
                  else {  // root namespace
                      $dir = "/";
                      $ns = ":";
                  }
                 $fn = md5($matches[3]) . ".$ext";
                 $path = $conf["mediadir"] . $dir .  $fn;   
                  }
                  else { 
                      $path =  $conf["mediadir"] .  "/$fn";
                      $path = str_replace(':','/',$path);
                  }                                
                  
                 @io_makeFileDir($path);
                 if(!file_exists($path)) {
                    @file_put_contents($path, base64_decode($matches[3]));
                      global $lang;
                     $id = $dir .  $fn;
                     $id = str_replace("/",":",$id);
                     addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_CREATE, $lang["created"],"", null, strlen(base64_decode($matches[3])));
                 }
                 else {
                     msg("file for this image previousely saved",2);
                 }
                 $left = "{{";
                 $right = "}}";
                 if($matches[1]) $left .= $matches[1];
                 if($matches[4]) $right = $matches[4] . $right;
                 
                $retv = "$left" . $ns. $fn . "$right";              
                 return $retv;
             },
             $TEXT
             );
        }     
      $TEXT = str_replace('%%', "FCKGPERCENTESC",  $TEXT);
     
        if($deaccent || $preserve_enc) {
              $TEXT = preg_replace_callback('/^(.*?)(\[\[.*?\]\])*(.*?)$/ms', 
                   function($matches) {         
                         $matches[1] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[1]);
                         $matches[2] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[2]);
                         $matches[3] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[3]);
                         return $matches[1].$matches[2].$matches[3];            
                    },
                    $TEXT
                 );
        }
  
        $TEXT = rawurldecode($TEXT);
        $TEXT = preg_replace('/NOWIKI_%_NOWIKI_%_/', '%%',$TEXT);
        $TEXT = preg_replace('/URLENC_PERCENT/', '%',$TEXT); 
        $TEXT = preg_replace('/NOWIKI_(.)_/', '$1',$TEXT);
        
          /* preserve newlines in code blocks */
          $TEXT = preg_replace_callback(
            '/(<code>|<file>)(.*?)(<\/code>|<\/file>)/ms',
            function($matches) {         
                return  str_replace("\n", "__code_NL__",$matches[0]);
            },
            $TEXT
          );

        $TEXT = preg_replace('/^\s*[\r\n]$/ms',"__n__", $TEXT);
        $TEXT = preg_replace('/oIWIKIo|cIWIKIc/ms',"", $TEXT);
        $TEXT = preg_replace('/\r/ms',"", $TEXT);
        $TEXT = preg_replace('/^\s+(?=\^|\|)/ms',"", $TEXT);    
        $TEXT = preg_replace('/__n__/',"\n", $TEXT);
        $TEXT = str_replace("__code_NL__","\n", $TEXT);
        $TEXT = str_replace("FCKGPERCENTESC", '%%',  $TEXT);
        if($this->getConf('complex_tables')) {
            $TEXT = str_replace('~~COMPLEX_TABLES~~','',$TEXT);
        }
        $TEXT .= "\n";
        // Removes relics of markup characters left over after acronym markup has been removed
        //$TEXT = preg_replace('/([\*\/_]{2})\s+\\1\s*([A-Z]+)\s*\\1+/ms',"$2",$TEXT);
      
         $pos = strpos($TEXT, 'MULTI_PLUGIN_OPEN');
         if($pos !== false) {
            $TEXT = preg_replace_callback(
             '|MULTI_PLUGIN_OPEN.*?MULTI_PLUGIN_CLOSE|ms',
             function($matches) {
                   return  preg_replace("/\\\\\\\\/ms","\n",$matches[0]);
             },
             $TEXT
           );

            $TEXT = preg_replace_callback(
             '|MULTI_PLUGIN_OPEN.*?MULTI_PLUGIN_CLOSE|ms',
             function($matches) {
                   return  preg_replace("/^\s+/ms","",$matches[0]);
             },
             $TEXT
           );
          $TEXT = str_replace("~~MULTI_PLUGIN_OPEN~~","~~MULTI_PLUGIN_OPEN~~\n",$TEXT);
         }

       if(strpos($TEXT,'L_PARgr') !== false) {        
            $TEXT = preg_replace_callback(
                 '|\(\((.*?)\)\)|ms',
                 function($matches) {
                       return  "((" . trim($matches[1]) . "))"; 
                 },
                 $TEXT
             );         
            $TEXT = str_replace('L_PARgr', '(',$TEXT);
            $TEXT = str_replace('R_PARgr', ')',$TEXT);
       } 
/*
Restructure numbered syntax highlighting  13/09/2019
*/
$TEXT = preg_replace_callback("#<code\s+(\w+)>.*?(\[enable_line_numbers.*?\])\s*\*\/#ms",
    function($matches) {
        return '<code ' . $matches[1] .' ' . $matches[2] .'>';
    }, $TEXT
) ;
       
        $this->replace_entities();
 /*Remove urls from linkonly images inserted after second and additional saves, resulting in multiple urls  corrupting  HTML output */
        $TEXT = preg_replace("/\{\{http:\/\/.*?fetch.php\?media=(.*?linkonly.*?)\}\}/",'{{' . "$1" .'}}',$TEXT);
        $TEXT = str_replace('< nowiki >', '%%<nowiki>%%',$TEXT);

 
          $TEXT = preg_replace_callback(
           '#\[\[(.*?)\]\]#ms',
               function($matches){ 
                    if($this->helper->has_plugin('button') && strpos($matches[0], '[[{') === 0) {    
                        return $matches[0];
                    }
                    if(preg_match('/[\w\.]+\s*>/',$matches[0])) {
                        return $matches[0];
                    }
	 	             if(preg_match('/([\w\.\-]+@[\w\.\-]+\.\w{2,3})\?.*?\|\1/i',$matches[0])) {
                             return $matches[0];
                     }
                    global $ID, $conf;      
                    $qs = "";
                    if(preg_match("/\[\[http/",$matches[0])) return $matches[0];  //not an internal link
                      if(preg_match("#\[\[.*?\|\{\{.*?\}\}\]\]#", $matches[0],$matches_1)) {  // media file
                        if(!$this->getConf('rel_links')) { 
                            return $matches[0];
                        }
                      $link = explode('?',$matches[1]); 
                      list($link_id,$linktext) = explode('|', $link[0]); 
                      $current_id = $this->abs2rel($link_id,$ID); 
                      return preg_replace("#$link_id#",$current_id, $matches[0]);                      
                   }                
                
                   $link = explode('?',$matches[1]);
                   if($link[1]) {                       
                       $link_id = $link[0];
                       list($qs,$linktext) = explode('|', $link[1]);     
                   }
                   else list($link_id,$linktext) = explode('|', $link[0]);     
                   if($this->getConf('rel_links')) 
                      $current_id = $this->abs2rel($link_id,$ID); 
                    else  $current_id = $link_id;
                    if($qs) $current_id .= "?$qs";
                    
                   //as in _getLinkTitle in xhtml.php
                   if(useHeading('content')) {
                      $tmp_linktext = p_get_first_heading($link_id);
                      if(trim($linktext) == trim($tmp_linktext)) {
                          $linktext = "";
                      }
                   }  
                   $tmp_ar = explode(':',$link_id);
                   $tmp_id = array_pop($tmp_ar);
                   if(!useHeading('content') && (trim($linktext,'.: ' ) == trim($tmp_id,'.: '))) 
					   $linktext = "";
                              
                   $current_id = $current_id.'|'.$linktext;
                   return '[[' . $current_id .']]';
               },
           $TEXT
         );      
      
        if($this->getConf('rel_links')) {    
          $TEXT = preg_replace_callback(
           '#\{\{(\s*)(.*?)(\s*)\}\}#ms',
           function($matches) {              
                global $ID;
               $link = explode('?',$matches[2]);
               list($link_id,$linktext) = explode('|', $link[0]);          
               $rel = $this->abs2rel($link_id,$ID);
               if(!empty($link[1])) $rel .= '?' . $link[1];
               if(!empty($linktext)) $rel = $rel.'|'.$linktext;
               return '{{' .$matches[1] . $rel . $matches[3]  .'}}';
           },
           $TEXT
         );               
        }

/* 11 Dec 2013 see comment below        
Remove discarded font syntax    
*/
        $TEXT = preg_replace_callback(
            '|_REMOVE_FONTS_START_(.*?)_REMOVE_FONTS_END_|ms',
            function($matches) {
                $matches[1] = preg_replace("/<font.*?>/ms","",$matches[1]);
                return preg_replace("/<\/font>/ms","",$matches[1]);
            },
            $TEXT
        );

 /* 
6 April 2013
Removed newlines and spaces from beginnings and ends of text enclosed by font tags.  Too subtle for javascript. 
 */
        $TEXT = preg_replace_callback(
         '|(<font.*?>)(.*?)(?=</font>)|ms',
           function($matches) {
               $matches[2]=preg_replace("/^\s+/ms","",$matches[2]);
               $matches[2]=preg_replace("/\s+$/ms","",$matches[2]);              
               return $matches[1]. $matches[2];
         },
         $TEXT
       );
       /* insure space before and after ckgedit font oprning and closing tags*/
       $TEXT = preg_replace("/<font.*?>\s+<\/font>/","", $TEXT); 
       $TEXT = preg_replace("/<font/ms"," <font", $TEXT); // add space
       $TEXT = preg_replace("/<\/font>/ms","</font> ", $TEXT);
       $TEXT = preg_replace('/\s{2,}<font/ms',"\n <font",$TEXT); // remove duplicate spaces
       $TEXT = preg_replace('/font>{2,}/ms',' font> ',$TEXT);
       
       $TEXT = preg_replace('/__QUOTE__/ms',">",$TEXT);
       $TEXT = preg_replace('/[\t\x20]+$/ms',"",$TEXT);
       $TEXT = preg_replace('/\n{4,}/ms',"\n\n",$TEXT);
       $TEXT = preg_replace('/\n{3,}/ms',"\n\n",$TEXT);
                  
      /*first pass for blockquotes*/                         
      $TEXT  = preg_replace_callback(
      "#^>+(.*?)\\\\\\\\#ms",
        function($matches) {      
            return str_replace('\\',"",$matches[0]);
        },
        $TEXT 
    );

      /* remove extra line-feeds following in-table code blocks
         make sure cell-ending pipe not mistaken for a following link divider      
      */ 
      $TEXT = preg_replace_callback(
       '#(/code|/file)\>.*?\n\|#ms',  
       function($matches) {         
                $matches[0] = preg_replace("/([\S\s\w\:])\\\\\\\\(\w)/ms","$1@#@$2",$matches[0]); //retain backslashes inside code blocks
                $matches[0] = preg_replace("/(\w+)\\\\\\\\(\w)/ms","$1@#@",$matches[0]);
                $matches[0] = preg_replace("/\\\\(\w+)/ms","@!@$1",$matches[0]);
                $matches[0] = preg_replace("/(\w+)\\\\/ms","@!@$1",$matches[0]);               
         $matches[0] =  str_replace("\\", "",$matches[0]);              
                $matches[0] =  str_replace("@!@",'\\',$matches[0]);
         return str_replace("@#@", "\\\\",$matches[0]);              
      },
      $TEXT     
      );
      /* reformat table cell after removing extra line-feeds, above */
    $TEXT = preg_replace_callback(  
         '#\|[\s\n]+(\<file.*?\>)(.*?)(\<\/file>\s*.*?)\n?\|#ms',   
         function($matches) {  
            //$ret =  '</' . $matches[1] . '>' . str_replace('\\',"",$matches[2]) . '|'; 
             $matches[3]  = preg_replace('/\n+/',"",$matches[3] );
             $matches[3]  = preg_replace('/\s+$/',"",$matches[3] ) . '|';     
             return '|' . $matches[1]  . $matches[2]  . str_replace("\\ ","",$matches[3]);
         },
         $TEXT     
    );
    

        /*  Feb 23 2019
	remove spaces and line feeds between beginning of table cell and start of code block
	*/
      $TEXT = preg_replace_callback(
       '#\|(.*?)[\s\n]+\<(code|file)\>#ms',  
       function($matches) { 
	   return '|' . $matches[1] ."\n<". $matches[2] .'>' . "\n";         
       },$TEXT
       );
     /*remove line feeds following block */
    $TEXT = preg_replace_callback(     
	   '#\<\/(code|file)\>([^\w\|]+)#ms', 
       function($matches) {  
       $matches[2] = str_replace(':\\', '~~WIN__DIR~~',$matches[2]);     
       $matches[2] = preg_replace('#([\w;.:=\:])\\\\#ms', "$1_bSL_",$matches[2]);  //protect backslashes in Windows paths
       $ret = '</' . $matches[1] . '>' . str_replace("\\","",$matches[2]);  
       $ret = str_replace( '_bSL_', '\\',$ret); 
       $ret = str_replace( '~~WIN__DIR~~', ':\\',$ret); 
          return "\n" .$ret;  
       },$TEXT
       ); 
	  $TEXT = str_replace('CBL__Bksl','\\',$TEXT);
	  $TEXT = preg_replace("/<code\s+file/ms",'<code ',$TEXT);
	  
        $TEXT = preg_replace('#((\\\\){2}\s*)$#', "",$TEXT);
        
         return;
    
    }

 function get_imgpaste_fname($ext) {
        global $ID;
        if(!$this->helper->has_plugin('imgpaste')) return;
        if(!$this->getConf('imgpaste')) return;
        $imgpaste = plugin_load('action','imgpaste'); 
        $filename = $imgpaste->getConf('filename');
        if(!$filename) return false;
        $filename = str_replace(
                        array(
                             '@NS@',
                             '@ID@',
                             '@USER@',
                             '@PAGE@'
                        ),
                        array(
                             getNS($ID),// getNS($INPUT->post->str('id')),
                             $ID,// $INPUT->post->str('id'),
                             $_SERVER['REMOTE_USER'],
                             noNS($ID)  //noNS($INPUT->post->str('id'))
                        ),
                        $filename
                    );
        $filename  = date(str_replace(array('%Y','%m','%d','%H','%M','%S','%y'), array('Y','m','d','H','i','s','y'), $filename));    
        $filename = cleanID($filename);
        return $filename . '.' . $ext;
}

function replace_entities() {
global $TEXT;
global $ents;
    $serialized = FCK_ACTION_SUBDIR . 'ent.ser';
    $ents = unserialize(file_get_contents($serialized), ['allowed_classes' => false]);

       $TEXT = preg_replace_callback(
            '|(&(\w+);)|',
            function($matches) {
                global $ents; return $ents[$matches[2]];
            },          
            $TEXT
        );
    
}


function write_debug($data) {
  return;
  if (!$handle = fopen(DOKU_INC . 'save.txt', 'a')) {
    return;
    }

    // Write $somecontent to our opened file.
    fwrite($handle, "save.php: $data\n");
    fclose($handle);

}
/* @auth Sergey Kotov */
//linkPath is the link in the page
//pagePath is absolute path of the page (ns1:ns2:....:page or :ns1:ns2:....:page)
function abs2rel($linkPath,$pagePath){
    if ($linkPath[0]==='.'){
        // It's already relative
        return $linkPath;
    }
    $aLink=explode(':',$linkPath);
    $nLink=count($aLink);
    if ($nLink<2){
        return $linkPath;
    }
    $aPage=explode(':',$pagePath);
    if(empty($aLink[0])) {
        // If linkPath is started by ':'
        // Make canonical absolute path ns1:ns2:.....:pageLink (strip leading :)
        array_shift($aLink);
        if (--$nLink<2) {
            return $linkPath;
        }
    }
    
    if(empty($aPage[0])) {
        // If pagePath is started by ':'
        // Make canonical absolute path ns1:ns2:.....:page (strip leading :)
        array_shift($aPage);
    }
    $nPage=count($aPage);
    $nslEqual=0; // count of equal namespaces from left to right
    // Minimal length of these two arrays, page name is not included
    $nMin=($nLink<$nPage ? $nLink : $nPage)-1 ;
    for ($i=0;$i<$nMin;++$i){
        if ($aLink[$i]===$aPage[$i]){
            ++$nslEqual;
        }
        else {
            break;
        }
    }
    if ($nslEqual==0){
        // Link and page from different root namespaces
        return $linkPath;
    }
    // Truncate equal lef namespaces
    $aPageDiff=array_slice($aPage,$nslEqual);
    $nPageDiff=count($aPageDiff);
    $aLinkDiff=array_slice($aLink,$nslEqual);
    
    // Now we have to go up to nPageDiff-1 levels
    $aResult=array();
    if ($nPageDiff>1){
        $aResult=array_fill(0,$nPageDiff-1,'..');
    }
     else if($nPageDiff == 1) {
        $aResult[] = '.';
    }
    $aResult=array_merge($aResult,$aLinkDiff);
    return implode(':', $aResult);
}


} //end of action class
?>
