openrat-cms

OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

commit 42ec67aa39e543f0b168393ef217937647a469d3
parent 49ef887d824c27977862c700c5d7a74460b1a8b2
Author: Jan Dankert <develop@jandankert.de>
Date:   Fri, 18 Sep 2020 22:48:46 +0200

Refactoring: Every project has 1 publishing target.

Diffstat:
modules/cms/action/PageelementAction.class.php | 3---
modules/cms/base/DB.class.php | 17+++++++++++++++++
modules/cms/model/File.class.php | 5-----
modules/cms/model/Project.class.php | 50++++++++++++++++++++++++++++++++++++--------------
modules/cms/publish/FilePublisher.class.php | 23-----------------------
modules/cms/publish/FolderPublisher.class.php | 69---------------------------------------------------------------------
modules/cms/publish/Ftp.class.php | 203-------------------------------------------------------------------------------
modules/cms/publish/PublishPublic.class.php | 156++++++++++++++++----------------------------------------------------------------
modules/cms/publish/PublishShow.class.php | 52----------------------------------------------------
modules/cms/publish/require.php | 15---------------
modules/cms/publish/target/Dav.class.php | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Fax.class.php | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Ftp.class.php | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Ftps.class.php | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Local.class.php | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/NoTarget.class.php | 47+++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/README.md | 25+++++++++++++++++++++++++
modules/cms/publish/target/SFtp.class.php | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Scp.class.php | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/cms/publish/target/Target.class.php | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Url.class.php | 49+++++++++++++++++++++++++++++++++++++++++++++++++
21 files changed, 1033 insertions(+), 510 deletions(-)

diff --git a/modules/cms/action/PageelementAction.class.php b/modules/cms/action/PageelementAction.class.php @@ -13,11 +13,8 @@ use cms\model\Folder; use cms\model\BaseObject; use cms\publish\PublishEdit; use cms\publish\PublishPreview; -use cms\publish\PublishShow; use util\Html; -use util\Http; use LogicException; -use util\Session; use util\Transformer; use util\Text; use util\exception\ValidationException; diff --git a/modules/cms/base/DB.class.php b/modules/cms/base/DB.class.php @@ -0,0 +1,16 @@ +<?php + +namespace cms\base; + +class DB { + + /** + * Turns a SQL query into a Statement. + * + * @param $sql SQL-query + * @return \database\Statement SQL-Statement + */ + public static function sql( $sql ) { + return db()->sql( $sql ); + } +}+ \ No newline at end of file diff --git a/modules/cms/model/File.class.php b/modules/cms/model/File.class.php @@ -20,12 +20,7 @@ namespace cms\model; // Standard Mime-Type use cms\publish\filter\AbstractFilter; -use cms\publish\PublishEdit; -use cms\publish\PublishPreview; use cms\publish\PublishPublic; -use cms\publish\PublishShow; -use util\JSqueeze; -use \Less_Parser; use logger\Logger; use util\cache\FileCache; diff --git a/modules/cms/model/Project.class.php b/modules/cms/model/Project.class.php @@ -2,6 +2,7 @@ namespace cms\model; +use cms\base\DB; use database\Database; use util\Session; @@ -21,7 +22,6 @@ class Project extends ModelBase const FLAG_PUBLISH_PAGE_EXTENSION = 8; const FLAG_LINK_ABSOLUTE = 16; - // Eigenschaften public $projectid; public $name; @@ -289,9 +289,9 @@ class Project extends ModelBase // Speichern public function save() { - $db = db_connection(); + $this->cleanTarget(); - $sql = $db->sql( <<<SQL + $stmt = DB::sql( <<<SQL UPDATE {{project}} SET name = {name}, target_dir = {target_dir}, @@ -304,12 +304,12 @@ class Project extends ModelBase SQL ); - $sql->setString('ftp_url' ,$this->ftp_url ); - $sql->setString('url' ,$this->url ); - $sql->setString('name' ,$this->name ); - $sql->setString('target_dir' ,$this->target_dir ); - $sql->setInt ('ftp_passive' ,$this->ftp_passive ); - $sql->setString('cmd_after_publish' ,$this->cmd_after_publish ); + $stmt->setString('ftp_url' ,$this->ftp_url ); + $stmt->setString('url' ,$this->url ); + $stmt->setString('name' ,$this->name ); + $stmt->setString('target_dir' ,$this->target_dir ); + $stmt->setInt ('ftp_passive' ,$this->ftp_passive ); + $stmt->setString('cmd_after_publish' ,$this->cmd_after_publish ); $flags = 0; if( $this->cut_index) $flags |= self::FLAG_CUT_INDEX; @@ -318,10 +318,10 @@ SQL if( $this->publishPageExtension) $flags |= self::FLAG_PUBLISH_PAGE_EXTENSION; if( $this->linkAbsolute ) $flags |= self::FLAG_LINK_ABSOLUTE; - $sql->setInt ('flags' ,$flags ); - $sql->setInt ('projectid' ,$this->projectid ); + $stmt->setInt ('flags' ,$flags ); + $stmt->setInt ('projectid' ,$this->projectid ); - $sql->query(); + $stmt->query(); try { @@ -1061,6 +1061,29 @@ SQL { return $this->name; } + + + /** + * Cleans up the target url. + */ + private function cleanTarget() + { + $target = parse_url( $this->target_dir ); + + $scheme = isset($target['scheme']) ? $target['scheme'] . '://' : ''; + if ( empty($scheme) ) + $scheme = 'file:/'; + + $host = isset($target['host']) ? $target['host'] : ''; + $port = isset($target['port']) ? ':' . $target['port'] : ''; + $user = isset($target['user']) ? $target['user'] : ''; + $pass = isset($target['pass']) ? ':' . $target['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($target['path']) ? $target['path'] : ''; + $query = isset($target['query']) ? '?' . $target['query'] : ''; + $fragment = isset($target['fragment']) ? '#' . $target['fragment'] : ''; + + $this->target_dir = "$scheme$user$pass$host$port$path$query$fragment"; + } } -?>- \ No newline at end of file diff --git a/modules/cms/publish/FilePublisher.class.php b/modules/cms/publish/FilePublisher.class.php @@ -1,22 +0,0 @@ -<?php -/** - * Created by PhpStorm. - * User: dankert - * Date: 10.08.18 - * Time: 23:33 - */ -// UNUSED? -class FilePublisher -{ - public function publish() - { - if ( ! is_object($this->publish) ) - $this->publish = new \Publish(); - - $this->write(); - $this->publish->copy( $this->tmpfile(),$this->full_filename(),$this->lastchangeDate ); - - $this->publish->publishedObjects[] = $this->getProperties(); - } - -}- \ No newline at end of file diff --git a/modules/cms/publish/FolderPublisher.class.php b/modules/cms/publish/FolderPublisher.class.php @@ -1,68 +0,0 @@ -<?php - -use util\Text; - -/** - * Created by PhpStorm. - * User: dankert - * Date: 10.08.18 - * Time: 23:35 - */ -// UNUSED??? -class FolderPublisher -{ - function publish( $withPages,$withFiles,$subdirs = false ) - { - set_time_limit(300); - if ( ! is_object($this->publish) ) - $this->publish = new \Publish(); - - foreach( $this->getObjectIds() as $oid ) - { - $o = new BaseObject( $oid ); - $o->objectLoadRaw(); - - if ( $o->isPage && $withPages ) - { - $p = new Page( $oid ); - $p->load(); - $p->publish = &$this->publish; - $p->publish(); - } - - if ( $o->isFile && $withFiles ) - { - $f = new File( $oid ); - $f->load(); - $f->publish = &$this->publish; - $f->publish(); - } - - if ( $o->isImage && $withFiles ) - { - $f = new Image( $oid ); - $f->load(); - $f->publish = &$this->publish; - $f->publish(); - } - - if ( $o->isText && $withFiles ) - { - $f = new Text( $oid ); - $f->load(); - $f->publish = &$this->publish; - $f->publish(); - } - - if ( $o->isFolder && $subdirs ) - { - $f = new Folder( $oid ); - $f->load(); - $f->publish = &$this->publish; - $f->publish( $withPages,$withFiles,true ); - } - } - } - - -}- \ No newline at end of file diff --git a/modules/cms/publish/Ftp.class.php b/modules/cms/publish/Ftp.class.php @@ -1,202 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; 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. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -namespace cms\publish; - -use logger\Logger; -use util\exception\PublisherException; -use util\exception\UIException; - - -/** - * Darstellen einer FTP-Verbindung, das beinhaltet - * das Login, das Kopieren von Dateien sowie praktische - * FTP-Funktionen - * - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Ftp -{ - var $verb; - var $url; - var $log = array(); - - var $passive = false; - - var $ok = true; - - private $path; - - - // Konstruktor - public function __construct($url) - { - $this->connect($url); - } - - - // Aufbauen der Verbindung - private function connect($url) - { - $this->url = $url; - - global $conf; - - $conf_ftp = $conf['publish']['ftp']; - $ftp = parse_url($this->url); - - // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten - $ftp = array_merge($conf_ftp, $ftp); - - // Nur FTP und FTPS (seit PHP 4.3) erlaubt - if (!in_array(@$ftp['scheme'], array('ftp', 'ftps'))) { - throw new PublisherException('Unknown scheme in FTP Url: ' . @$ftp['scheme'] . - '. Only FTP (and FTPS, if compiled in) are supported'); - } - - if (function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps') - $this->verb = @ftp_ssl_connect($ftp['host'], $ftp['port']); - else - $this->verb = @ftp_connect($ftp['host'], $ftp['port']); - - if (!$this->verb) { - Logger::error('Cannot connect to ' . $ftp['host'] . ':' . $ftp['port']); - throw new PublisherException('Cannot connect to ' . $ftp['scheme'] . '-server: ' . $ftp['host'] . ':' . $ftp['port']); - } - - $this->log[] = 'Connected to FTP server ' . $ftp['host'] . ':' . $ftp['port']; - - if (empty($ftp['user'])) { - $ftp['user'] = 'anonymous'; - $ftp['pass'] = 'openrat@openrat.de'; - } - - if (!ftp_login($this->verb, $ftp['user'], $ftp['pass'])) - throw new PublisherException('Unable to login as user ' . $ftp['user']); - - $this->log[] = 'Logged in as user ' . $ftp['user']; - - $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive'); - - $this->log[] = 'entering passive mode ' . ($pasv ? 'on' : 'off'); - if (!ftp_pasv($this->verb, true)) - throw new PublisherException('Cannot switch to FTP PASV mode'); - - if (!empty($ftp['query'])) { - parse_str($ftp['query'], $ftp_var); - - if (isset($ftp_var['site'])) { - $site_commands = explode(',', $ftp_var['site']); - foreach ($site_commands as $cmd) { - if (!@ftp_site($this->verb, $cmd)) - throw new PublisherException('unable to do SITE command: ' . $cmd); - } - } - } - - $this->path = rtrim($ftp['path'], '/'); - - $this->log[] = 'Changing directory to ' . $this->path; - - if (!@ftp_chdir($this->verb, $this->path)) - throw new PublisherException('unable CHDIR to directory: ' . $this->path); - } - - - /** - * Kopieren einer Datei vom lokalen System auf den FTP-Server. - * - * @param String Quelle - * @param String Ziel - * @param int FTP-Mode (BINARY oder ASCII) - */ - public function put($source, $dest) - { - $dest = $this->path . '/' . $dest; - - $this->log .= "Copying file: $source -&gt; $dest ...\n"; - - $mode = FTP_BINARY; - $p = strrpos(basename($dest), '.'); // Letzten Punkt suchen - - if ($p !== false) // Wennn letzten Punkt gefunden, dann dort aufteilen - { - $extension = substr(basename($dest), $p + 1); - $type = config('mime-types', $extension); - if (substr($type, 0, 5) == 'text/') - $mode = FTP_ASCII; - } - - Logger::debug("FTP PUT target:$dest mode:" . (($mode == FTP_ASCII) ? 'ascii' : 'binary')); - - if (!@ftp_put($this->verb, $dest, $source, $mode)) { - if (!$this->mkdirs(dirname($dest))) - return; // Fehler. - - ftp_chdir($this->verb, $this->path); - - if (!@ftp_put($this->verb, $dest, $source, $mode)) - throw new PublisherException("FTP PUT failed.\n" . - "source : $source\n" . - "destination: $dest"); - - } - } - - - /** - * Private Methode zum rekursiven Anlegen von Verzeichnissen. - * - * @param String Pfad - * @return boolean true, wenn ok - */ - private function mkdirs($strPath) - { - if (@ftp_chdir($this->verb, $strPath)) - return true; // Verzeichnis existiert schon :) - - $pStrPath = dirname($strPath); - - if (!$this->mkdirs($pStrPath)) - return false; - - if (!@ftp_mkdir($this->verb, $strPath)) - throw new PublisherException("failed to create remote directory: $strPath"); - - return true; - } - - - /** - * Schliessen der FTP-Verbindung.<br> - * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. - */ - public function close() - { - if (!@ftp_quit($this->verb)) { - // Closing not possible. - // Only logging. Maybe we could throw an Exception here? - Logger::warn('Failed to close FTP connection. Continueing...'); - return; - } - } -} - - -?>- \ No newline at end of file diff --git a/modules/cms/publish/PublishPublic.class.php b/modules/cms/publish/PublishPublic.class.php @@ -9,9 +9,17 @@ use cms\model\Link; use cms\model\Page; use cms\model\Project; use cms\model\Url; +use cms\publish\target\Dav; +use cms\publish\target\Fax; +use cms\publish\target\Ftp; +use cms\publish\target\Ftps; +use cms\publish\target\Local; +use cms\publish\target\NoTarget; +use cms\publish\target\Scp; +use cms\publish\target\SFtp; +use cms\publish\target\Target; use util\exception\PublisherException; use util\FileUtils; -use cms\publish\Ftp; use logger\Logger; use util\exception\UIException; use util\Session; @@ -33,11 +41,11 @@ class PublishPublic extends Publish /** - * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn - * zu einem FTP-Server veroeffentlicht werden soll. - * @var Object + * The target to which the file will be copied to. + * + * @var Target */ - private $ftp; + private $target; private $localDestinationDirectory = ''; @@ -89,40 +97,22 @@ class PublishPublic extends Publish $this->linkSchema = ($project->linkAbsolute ? self::SCHEMA_ABSOLUTE : self::SCHEMA_RELATIVE); - // Feststellen, ob FTP benutzt wird. - // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein. - $ftpUrl = ''; - if ( $confPublish['ftp']['enable'] ) - { - if ( $confPublish['ftp']['per_project'] && !empty($project->ftp_url) ) - $ftpUrl = $project->ftp_url; - elseif ( !empty($confPublish['ftp']['host']) ) - $ftpUrl = $project->ftp_url; - } + $targetScheme = parse_url( $project->target_dir,PHP_URL_SCHEME ); - if ( $ftpUrl && $ftpUrl[0]!='#' ) - { - $this->ftp = new \cms\publish\Ftp($project->ftp_url); // Aufbauen einer FTP-Verbindung - - $this->ftp->passive = ( $project->ftp_passive == '1' ); - } - - $targetDir = rtrim( $project->target_dir,'/' ); - - if ( FileUtils::isAbsolutePath($targetDir) && $confPublish['filesystem']['per_project'] ) - { - $this->localDestinationDirectory = FileUtils::toAbsolutePath([$targetDir]); // Projekteinstellung verwenden. - } - else - { - // Konfiguriertes Verzeichnis verwenden. - $this->localDestinationDirectory = FileUtils::toAbsolutePath([$confPublish['filesystem']['directory'],$targetDir]); - } + $availableTargets = [ Local::class,Ftp::class,Ftps::class,Fax::class,SFtp::class,Scp::class,Dav::class ]; + /** @var Target $target */ + foreach($availableTargets as $target ) + { + if ( $target::isAvailable() && $target::accepts( $targetScheme )) + { + $this->target = new $target( $project->target_dir ); + break; + } + } - // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist. - if ( $this->localDestinationDirectory && $this->localDestinationDirectory[0] == '#') - $this->localDestinationDirectory = ''; + if ( empty( $this->target ) ) + throw new PublisherException('Cannot publish to the scheme '.$targetScheme ); $this->contentNegotiation = ( $project->content_negotiation == '1' ); $this->cutIndex = ( $project->cut_index == '1' ); @@ -142,10 +132,8 @@ class PublishPublic extends Publish if ( config('security','nopublish') ) { + $this->target = new NoTarget(); Logger::warn('publishing is disabled.'); - $this->commandAfterPublish = ''; - $this->localDestinationDirectory = ''; - $this->ftp = null; } } @@ -302,92 +290,11 @@ class PublishPublic extends Publish */ public function copy( $tmp_filename,$dest_filename,$lastChangeDate=null ) { - global $conf; - $source = $tmp_filename; - - - - if ( $this->localDestinationDirectory ) - { - // Is the output directory writable? - if ( !is_writeable( $this->localDestinationDirectory ) ) - throw new PublisherException('directory not writable: ' . $this->localDestinationDirectory); - - $dest = $this->localDestinationDirectory.'/'.$dest_filename; - - // Is the destination writable? - if ( is_file($dest) && !is_writeable( $dest ) ) - throw new PublisherException('file not writable: ' . $this->dest); - - // Copy file to destination - if (!@copy( $source,$dest )); - { - // Create directories, if necessary. - $this->mkdirs( dirname($dest) ); - - if (!@copy( $source,$dest )) - throw new PublisherException( 'failed copying local file:' . "\n" . - 'source : ' . $source . "\n" . - 'destination: ' . $dest); - - // Das Änderungsdatum der Datei auch in der Zieldatei setzen. - if ( $conf['publish']['set_modification_date'] ) - if ( ! is_null($lastChangeDate) ) - @touch( $dest,$lastChangeDate ); - - Logger::debug("published: $dest"); - } - - if (!empty($conf['security']['chmod'])) - { - // CHMOD auf der Datei ausfuehren. - if ( ! @chmod($dest,octdec($conf['security']['chmod'])) ) - throw new PublisherException('Unable to CHMOD file ' . $dest); - } - } - - if ( $this->ftp ) // Falls FTP aktiviert - { - $dest = $dest_filename; - $this->ftp->put( $source,$dest ); - } + $this->target->put($tmp_filename,$dest_filename,$lastChangeDate); } - /** - * Rekursives Anlagen von Verzeichnisse - * Nett gemacht. - * Quelle: http://de3.php.net/manual/de/function.mkdir.php - * Thx to acroyear at io dot com - * - * @param String Verzeichnis - * @return boolean - */ - private function mkdirs($path ) - { - global $conf; - - if ( is_dir($path) ) - return; // Path exists - - $parentPath = dirname($path); - - $this->mkdirs($parentPath); - - // - if ( ! @mkdir($path) ) - throw new PublisherException( 'Cannot create directory: ' . $path); - - // CHMOD auf dem Verzeichnis ausgef�hren. - if (!empty($conf['security']['chmod_dir'])) - { - if ( ! @chmod($path,octdec($conf['security']['chmod_dir'])) ) - throw new PublisherException('Unable to CHMOD directory: ' . $path); - } - } - - /** * Beenden des Ver�ffentlichungs-Vorganges.<br> @@ -396,11 +303,7 @@ class PublishPublic extends Publish */ public function close() { - if ( $this->ftp ) - { - Logger::debug('Closing FTP connection' ); - $this->ftp->close(); - } + $this->target->close(); // Ausfuehren des Systemkommandos. if ( !empty($this->commandAfterPublish) ) @@ -412,6 +315,7 @@ class PublishPublic extends Publish putenv("CMS_USER_NAME=".$user->name ); putenv("CMS_USER_ID=" .$user->userid); putenv("CMS_USER_MAIL=".$user->mail ); + exec( $this->commandAfterPublish,$ausgabe,$rc ); if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Fehler melden. diff --git a/modules/cms/publish/PublishShow.class.php b/modules/cms/publish/PublishShow.class.php @@ -1,51 +0,0 @@ -<?php - -namespace cms\publish; - -use cms\model\BaseObject; -use cms\model\Link; -use cms\model\Url; - -/** - * Created by PhpStorm. - * User: dankert - * Date: 10.08.18 - * Time: 23:47 - */ - -class PublishShow extends Publish -{ - /** - * @param $from \cms\model\BaseObject - * @param $to \cms\model\BaseObject - */ - public function linkToObject( BaseObject $from, BaseObject $to ) - { - return "..."; - } - - public function isPublic() - { - return false; - } - - public function copy($tmp_filename,$dest_filename,$lastChangeDate=null) - { - // nothing to do. - } - - public function clean() - { - // nothing to do. - } - - public function close() - { - // nothing to do. - } - - public function isSimplePreview() - { - return false; - } -}- \ No newline at end of file diff --git a/modules/cms/publish/require.php b/modules/cms/publish/require.php @@ -1,15 +0,0 @@ -<?php - -require_once( __DIR__.'/'.'Publish.class.php' ); -require_once(__DIR__ . '/'.'PublishPreview.class.php'); -require_once(__DIR__ . '/'.'PublishEdit.class.php'); -require_once(__DIR__ . '/'.'PublishShow.class.php'); -require_once(__DIR__ . '/'.'PublishPublic.class.php'); -require_once( __DIR__.'/'.'Ftp.class.php' ); - -require_once( __DIR__.'/'.'filter/AbstractFilter.class.php' ); -require_once( __DIR__.'/'.'filter/JavascriptMinifierFilter.class.php' ); -require_once( __DIR__.'/'.'filter/LessFilter.class.php' ); -require_once( __DIR__.'/'.'filter/Base64DecodeFilter.class.php' ); -require_once( __DIR__.'/'.'filter/Base64EncodeFilter.class.php' ); -require_once( __DIR__.'/'.'filter/Csv2HtmlFilter.class.php' ); diff --git a/modules/cms/publish/target/Dav.class.php b/modules/cms/publish/target/Dav.class.php @@ -0,0 +1,88 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; +use util\Url; + + +/** + * Publishing a file over WebDAV. + * + * @author Jan Dankert + */ +class Dav extends Target +{ + /** + * @var false|resource + */ + private $fp; + + public function checkConnection() + { + $content = "HEAD / HTTP/1.1\r\n"; + $content .= "Host: ".$this->url->host."\r\n"; + $content .= "Connection: Close\r\n"; + $content .= "\r\n"; + + fwrite($this->fp, $content ); + } + + + public function put($source, $dest, $time) + { + $content = "PUT $dest HTTP/1.1\r\n"; + $content .= "Host: ".$this->url->host."\r\n"; + $content .= "Connection: Close\r\n"; + $content .= "\r\n"; + + fwrite($this->fp, $content); + fwrite($this->fp, file_get_contents($source)); + } + + + public function mkcol( $dir ) { + $content = "MKCOL $dir HTTP/1.1\r\n"; + $content .= "Host: ".$this->url->host."\r\n"; + $content .= "Connection: Close\r\n"; + $content .= "\r\n"; + fwrite($this->fp, $content); + } + + public function close() + { + fclose($this->fp); + } + + protected static function acceptsSchemes() + { + return ['dav']; + } + + public function open() + { + $this->fp = fsockopen($this->url->host, empty($this->url->port)?80:$this->url->port, $errno, $errstr, 5); + + if(!$this->fp) + throw new PublisherException("cannot connect to DAV server: $errno -> $errstr"); + + $this->checkConnection(); + } +}+ \ No newline at end of file diff --git a/modules/cms/publish/target/Fax.class.php b/modules/cms/publish/target/Fax.class.php @@ -0,0 +1,57 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * Publishing a file over fax. Who says that this is not possible? + * + * @author Jan Dankert + */ +class Fax extends Target +{ + public function open() + { + Logger::debug("Dialing ..."); + } + + public function put($source, $dest, $time) + { + Logger::debug("düüüüüüüüüüüükrrrkkrkrkrkkrkrkrkr"); + // very, very funny, right? + } + + public function close() + { + Logger::debug("Hanging up ..."); + } + + protected static function acceptsSchemes() + { + return ['fax']; + } + + public function __construct($targetUrl) + { + } +} +// ok, this class was a joke.+ \ No newline at end of file diff --git a/modules/cms/publish/target/Ftp.class.php b/modules/cms/publish/target/Ftp.class.php @@ -0,0 +1,178 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * FTP-Target. + * + * @author Jan Dankert + */ +class Ftp extends Target +{ + private $connection; + + public static function acceptsSchemes() { + return ['ftp']; + } + + // Aufbauen der Verbindung + public function open() + { + //global $conf; + //$conf_ftp = $conf['publish']['ftp']; + $ftp = $this->url; + + // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten + //$ftp = array_merge($conf_ftp, $ftp); + + $this->connection = $this->createConnection(); + + if (!$this->connection) { + Logger::error('Cannot connect to ' . $this->url->host . ':' . $this->url->port); + throw new PublisherException('Cannot connect to ' . $this->url->scheme . '-server: ' . $this->url->host . ':' . $this->url->port); + } + + if (empty($this->url->user)) { + $ftp['user'] = 'anonymous'; + $ftp['pass'] = 'openrat@openrat.de'; + } + + if (!ftp_login($this->connection, $this->url->user, $this->url->pass)) + throw new PublisherException('Unable to login as user ' . $this->url->user); + + $pasv = $this->url->fragment == 'passive'; + + if ( $pasv ) + if (!ftp_pasv($this->connection, true)) + throw new PublisherException('Cannot switch to FTP PASV mode'); + + if ( $this->url->query ) { + parse_str($this->url->query, $ftp_var); + + if (isset($ftp_var['site'])) { + $site_commands = explode(',', $ftp_var['site']); + foreach ($site_commands as $cmd) { + if (!@ftp_site($this->connection, $cmd)) + throw new PublisherException('unable to do SITE command: ' . $cmd); + } + } + } + + $path = rtrim($this->url->path, '/'); + + if (!@ftp_chdir($this->connection, $path)) + throw new PublisherException('unable CHDIR to directory: ' . $path); + } + + + /** + * Kopieren einer Datei vom lokalen System auf den FTP-Server. + * + * @param String Quelle + * @param String Ziel + * @param int FTP-Mode (BINARY oder ASCII) + */ + public function put($source, $dest, $lastChangeDate) + { + $dest = $this->url->path . '/' . $dest; + + //$this->log .= "Copying file: $source -&gt; $dest ...\n"; + + $mode = FTP_BINARY; + $p = strrpos(basename($dest), '.'); // Letzten Punkt suchen + + if ($p !== false) // Wennn letzten Punkt gefunden, dann dort aufteilen + { + $extension = substr(basename($dest), $p + 1); + $type = config('mime-types', $extension); + if (substr($type, 0, 5) == 'text/') + $mode = FTP_ASCII; + } + + Logger::debug("FTP PUT target:$dest mode:" . (($mode == FTP_ASCII) ? 'ascii' : 'binary')); + + if (!@ftp_put($this->connection, $dest, $source, $mode)) { + if (!$this->mkdirs(dirname($dest))) + return; // Fehler. + + ftp_chdir($this->connection, $this->url->path); + + if (!@ftp_put($this->connection, $dest, $source, $mode)) + throw new PublisherException("FTP PUT failed.\n" . + "source : $source\n" . + "destination: $dest"); + + } + } + + + /** + * Private Methode zum rekursiven Anlegen von Verzeichnissen. + * + * @param String Pfad + * @return boolean true, wenn ok + */ + private function mkdirs($strPath) + { + if (@ftp_chdir($this->connection, $strPath)) + return true; // Verzeichnis existiert schon :) + + $pStrPath = dirname($strPath); + + if (!$this->mkdirs($pStrPath)) + return false; + + if (!@ftp_mkdir($this->connection, $strPath)) + throw new PublisherException("failed to create remote directory: $strPath"); + + return true; + } + + + /** + * Schliessen der FTP-Verbindung.<br> + * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. + */ + public function close() + { + if (!@ftp_quit($this->connection)) { + // Closing not possible. + // Only logging. Maybe we could throw an Exception here? + Logger::warn('Failed to close FTP connection. Continueing...'); + return; + } + } + + protected function createConnection() + { + return ftp_connect($this->url->host, $this->url->port); + } + + public static function isAvailable() + { + return function_exists('ftp_connect'); + } +} + + +?>+ \ No newline at end of file diff --git a/modules/cms/publish/target/Ftps.class.php b/modules/cms/publish/target/Ftps.class.php @@ -0,0 +1,53 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * Publishing via FTPS. + * + * @author Jan Dankert + */ +class Ftps extends Ftp +{ + public static function acceptsSchemes() { + return ['ftps']; + } + + /** + * Creates a new connection. + * + * @param $ftp + * @return false|resource + */ + protected function createConnection() + { + return ftp_ssl_connect($this->url->host, $this->url->port); + } + + + public static function isAvailable() + { + return function_exists('ftp_ssl_connect'); + + } +}+ \ No newline at end of file diff --git a/modules/cms/publish/target/Local.class.php b/modules/cms/publish/target/Local.class.php @@ -0,0 +1,152 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; +use util\FileUtils; +use util\Url; + + +/** + * Publishing to the local filesystem. + * + * @author Jan Dankert + */ +class Local extends Target +{ + /** + * @var string + */ + private $localDestinationDirectory; + + public static function acceptsSchemes() { + return ['file','']; + } + + + /** + * @param $url Url + */ + public function open() + { + $confPublish = config('publish'); + + $targetDir = rtrim( $this->url->path,'/' ); + + if ( FileUtils::isAbsolutePath($targetDir) && $confPublish['filesystem']['per_project'] ) + { + $this->localDestinationDirectory = FileUtils::toAbsolutePath([$targetDir]); // Projekteinstellung verwenden. + } + else + { + // Konfiguriertes Verzeichnis verwenden. + $this->localDestinationDirectory = FileUtils::toAbsolutePath([$confPublish['filesystem']['directory'],$targetDir]); + } + + + // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist. + if ( $this->localDestinationDirectory && $this->localDestinationDirectory[0] == '#') + $this->localDestinationDirectory = ''; + + } + + /** + * Copying a file to local filesystem. + * + * @param String Quelle + * @param String Ziel + */ + public function put($source, $dest, $lastChangeDate) + { + global $conf; + + // Is the output directory writable? + if ( !is_writeable( $this->localDestinationDirectory ) ) + throw new PublisherException('directory not writable: ' . $this->localDestinationDirectory); + + $dest = $this->localDestinationDirectory.'/'.$dest; + + // Is the destination writable? + if ( is_file($dest) && !is_writeable( $dest ) ) + throw new PublisherException('file not writable: ' . $dest); + + // Copy file to destination + if (!@copy( $source,$dest )); + { + // Create directories, if necessary. + $this->mkdirs( dirname($dest) ); + + if (!@copy( $source,$dest )) + throw new PublisherException( 'failed copying local file:' . "\n" . + 'source : ' . $source . "\n" . + 'destination: ' . $dest); + + // Das Änderungsdatum der Datei auch in der Zieldatei setzen. + if ( $conf['publish']['set_modification_date'] ) + if ( ! is_null($lastChangeDate) ) + @touch( $dest,$lastChangeDate ); + + Logger::debug("published: $dest"); + } + + if (!empty($conf['security']['chmod'])) + { + // CHMOD auf der Datei ausfuehren. + if ( ! @chmod($dest,octdec($conf['security']['chmod'])) ) + throw new PublisherException('Unable to CHMOD file ' . $dest); + } + + } + + + + /** + * Rekursives Anlagen von Verzeichnisse + * Nett gemacht. + * Quelle: http://de3.php.net/manual/de/function.mkdir.php + * Thx to acroyear at io dot com + * + * @param String Verzeichnis + * @return boolean + */ + private function mkdirs($path ) + { + global $conf; + + if ( is_dir($path) ) + return; // Path exists + + $parentPath = dirname($path); + + $this->mkdirs($parentPath); + + // + if ( ! @mkdir($path) ) + throw new PublisherException( 'Cannot create directory: ' . $path); + + // CHMOD auf dem Verzeichnis ausgef�hren. + if (!empty($conf['security']['chmod_dir'])) + { + if ( ! @chmod($path,octdec($conf['security']['chmod_dir'])) ) + throw new PublisherException('Unable to CHMOD directory: ' . $path); + } + } + +}+ \ No newline at end of file diff --git a/modules/cms/publish/target/NoTarget.class.php b/modules/cms/publish/target/NoTarget.class.php @@ -0,0 +1,46 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * Empty target. + * + * @author Jan Dankert + */ +class NoTarget extends Target +{ + public static function acceptsSchemes() + { + return ['null', 'example']; + } + + public function put($source, $dest, $lastChangeDate) + { + // Do nothing. + } + + public function __construct($targetUrl) + { + + } +}+ \ No newline at end of file diff --git a/modules/cms/publish/target/README.md b/modules/cms/publish/target/README.md @@ -0,0 +1,24 @@ +# Targets + +While publishing files and pages are pushed to a target. + +Possible publishing targets are +- Local filesystem +- FTP,FTPS +- WebDAV +- SCP +- SFTP + +The corresponding scheme names are +- `file` +- `ftp` +- `ftps` +- `dav` +- `scp` +- `sftp` + +The target is selected by the scheme in the target url in the project properties. + +## Example + +`scp://user@host/var/www` is publishing all files via SCP to the SSH-Server on host 'host'. + \ No newline at end of file diff --git a/modules/cms/publish/target/SFtp.class.php b/modules/cms/publish/target/SFtp.class.php @@ -0,0 +1,112 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * Darstellen einer FTP-Verbindung, das beinhaltet + * das Login, das Kopieren von Dateien sowie praktische + * FTP-Funktionen + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class SFtp extends Scp +{ + /** + * @var resource + */ + protected $sftpConnection; + + + public static function acceptsSchemes() { + return ['sftp']; + } + + // Aufbauen der Verbindung + public function open() + { + global $conf; + + //$conf_ftp = $conf['publish']['sftp']; + + // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten + //$sftp = array_merge($conf_ftp, $sftp); + + + $this->createConnection(); + + $this->sftpConnection = @ssh2_sftp($this->sshConnection); + + if (! $this->sftpConnection) + throw new PublisherException("Could not initialize SFTP subsystem."); + + } + + + /** + * Kopieren einer Datei vom lokalen System auf den SFTP-Server. + * + * @param String Quelle + * @param String Ziel + * @param int time) + */ + public function put($source, $dest, $lastChangeDate) + { + $dest = $this->url->path . '/' . $dest; + + $sftp = $this->sftpConnection; + + $stream = @fopen("ssh2.sftp://$sftp$dest", 'w'); + + if (! $stream) + throw new PublisherException("Could not create SFTP-Stream on file: $dest"); + + $data_to_send = @file_get_contents($source); + + if ($data_to_send === false) + throw new PublisherException("Could not open local file: $source"); + + if (@fwrite($stream, $data_to_send) === false) + throw new PublisherException("Could not send data from file: $source."); + + @fclose($stream); + } + + + /** + * Schliessen der FTP-Verbindung.<br> + * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. + */ + public function close() + { + parent::close(); + } + + + public static function isAvailable() + { + return parent::isAvailable() && function_exists('ssh2_sftp'); + } +} + diff --git a/modules/cms/publish/target/Scp.class.php b/modules/cms/publish/target/Scp.class.php @@ -0,0 +1,104 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; + + +/** + * Darstellen einer FTP-Verbindung, das beinhaltet + * das Login, das Kopieren von Dateien sowie praktische + * FTP-Funktionen + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Scp extends Target +{ + /** + * @var resource + */ + protected $sshConnection; + + public static function acceptsSchemes() { + return ['scp']; + } + + // Aufbauen der Verbindung + public function open() + { + $this->sshConnection = $this->createConnection(); + } + + + + protected function createConnection() { + + $sshConnection = @ssh2_connect($this->url->host,$this->url->port ); + + if (! $sshConnection) + throw new PublisherException("Could not connect to ".$this->url ); + + + if (! @ssh2_auth_password($sshConnection, $this->url->user,$this->url->pass) ) + throw new PublisherException("Could not authenticate"); + + $this->sshConnection = $sshConnection; + + return $sshConnection; + } + + + + /** + * Kopieren einer Datei vom lokalen System auf den FTP-Server. + * + * @param String Quelle + * @param String Ziel + * @param int FTP-Mode (BINARY oder ASCII) + */ + public function put($source, $dest, $lastChangeDate) + { + $dest = $this->url->path . '/' . $dest; + + + ssh2_scp_send($this->sshConnection, $source, $dest, 0644); + } + + + /** + * Schliessen der FTP-Verbindung.<br> + * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. + */ + public function close() + { + ssh2_disconnect($this->sshConnection); + } + + + public static function isAvailable() + { + return function_exists('ssh2_connect'); + } +} + + +?>+ \ No newline at end of file diff --git a/modules/cms/publish/target/Target.class.php b/modules/cms/publish/target/Target.class.php @@ -0,0 +1,79 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +namespace cms\publish\target; + +use logger\Logger; +use util\exception\PublisherException; +use util\exception\UIException; +use util\Url; + + +/** + * Superclass for publication targets. + * + * @author Jan Dankert + */ +abstract class Target +{ + /** + * @var Url + */ + protected $url; + + public function __construct( $targetUrl ) { + $this->url = new Url( $targetUrl ); + + $this->open(); + } + + + public static function accepts( $scheme ) { + return in_array( $scheme, static::acceptsSchemes() ); + } + + + /** + * For which types this target is reponsible? + * + * @return array + */ + protected abstract static function acceptsSchemes(); + + + public function open() { + + } + + + public abstract function put($source, $dest, $timestamp); + + + /** + * Closes the connection. + */ + public function close() { + + } + + + public static function isAvailable() { + + return true; + } + +} diff --git a/modules/util/Url.class.php b/modules/util/Url.class.php @@ -0,0 +1,48 @@ +<?php + +namespace util; + +class Url { + + public $scheme; + public $host; + public $port; + public $user; + public $pass; + public $path; + public $query; + public $fragment; + + function __construct( $url = null ) { + + if ( !empty($url) ) + $this->parseUrl($url); + } + + + public function parseUrl( $url ) { + foreach ( parse_url($url) as $key=>$value ) + $this->$key = $value; + } + + + public function __toString() + { + $scheme = !empty($this->scheme) ? $this->scheme . '://' : ''; + + if ( empty($scheme) ) + $scheme = 'file:/'; + + $host = !empty($this->host ) ? $this->host : ''; + $port = !empty($this->port ) ? ':' . $this->port : ''; + $user = !empty($this->user ) ? $this->user : ''; + $pass = !empty($this->pass ) ? ':' . $this->pass : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = !empty($this->path ) ? $this->path : ''; + $query = !empty($this->query ) ? '?' . $this->query : ''; + $fragment = !empty($this->fragment) ? '#' . $this->fragment : ''; + + return "$scheme$user$pass$host$port$path$query$fragment"; + } + +}+ \ No newline at end of file