#!/usr/bin/php -q * @revision 01 * @license http://creativecommons.org/licenses/by-sa/3.0/de/ Creative Commons Attribution-Share Alike 3.0 Germany * @homepage http://oss.tiggerswelt.net * @copyright Copyright © 2010 tiggersWelt.net **/ // Define our role define ('ROLE_SUBMITTER', 1); define ('ROLE_RECEIVER', 2); // Define some response-types define ('RESPOND_OK', 1); define ('RESPOND_ERROR', 2); define ('RESPOND_FATAL', 3); // {{{ respond /** * Write a response-code to the line * * @param enum $Say (optional) The response-code * @param string $Msg (optional) Append an error-message * * @access public * @return bool **/ function respond ($Say = RESPOND_OK, $Msg = '') { switch ($Say) { case RESPOND_OK: return (fwrite (STDOUT, chr (0)) !== false); case RESPOND_FATAL: return (fwrite (STDOUT, chr (2)) !== false); case RESPOND_ERROR: return (fwrite (STDOUT, chr (1) . (strlen ($Msg) > 0 ? 'Error: ' . $Msg : '') . "\n") !== false); } return false; } // }}} // {{{ readResponse /** * Read and parse a response from the other side * * @access public * @return enum **/ function readResponse () { // Read a single response-byte from stream $c = fread (STDIN, 1); // Handle OK-Response if ($c == chr (0)) return RESPOND_OK; // Handle fatal error if ($c == chr (2)) return RESPOND_FATAL; // THIS should never happen! if ($c != chr (1)) return RESPOND_OK; // Read the error-message fgets (STDIN); return RESPOND_ERROR; } // }}} // {{{ receive /** * Receive files and store them * * @param array $argv An array with exactly one item containing the destination-directory * * @access public * @return bool **/ function receive ($argv) { global $Preserve, $Recursive, $RequireDirectory, $Verbose; // We expect one arguement (our destination directory) if (count ($argv) != 1) return false; // Retrive the destination $baseDirectory = array_shift ($argv); $subDirectory = ''; if ($RequireDirectory && !is_dir ($baseDirectory)) return false; if ($baseDirectory [strlen ($baseDirectory) - 1] != '/') $baseDirectory .= '/'; // Send initial ACK respond (); // Times $aTime = time (); $mTime = time (); // Handle incoming files while (true) { // Read file-information if (!($Info = fgets (STDIN, 1024))) break; if ($Verbose) fwrite (STDERR, 'mySink: ' . $Info); // Handle the request $Action = $Info [0]; // This is an error if ($Action == chr (2)) return false; elseif ($Action == chr (1)) continue; $Info = explode (' ', rtrim (substr ($Info, 1))); switch ($Action) { case 'T': // T{MTIME} 0 {ATIME} 0 - Set times for File $mTime = intval ($Info [0]); $aTime = intval ($Info [2]); respond (); continue; case 'E': // Exit directory // Check if we are on a subdirectory if (strlen ($subDirectory) == 0) return true; // Move one level up $subDirectory = dirname ($subDirectory); if (($subDirectory == '.') || ($subDirectory == '/')) $subDirectory = '/'; else $subDirectory .= '/'; fwrite (STDERR, 'New subdir: ' . $subDirectory . "\n"); respond (); continue; case 'D': // D{MODE} 0 {NAME} - Change/Create subdirectory case 'C': // C{MODE} {SIZE} {NAME} - Change/Create file $Mode = octdec ($Info [0]); $Size = intval ($Info [1]); $Name = basename ($Info [2]); // Just to be sure if (($Name == '..') || ($Name != $Info [2])) { respond (RESPOND_ERROR, 'Invalid filename'); continue; } // Change/Create subdirectory if ($Action == 'D') { if (!$Recursive) { respond (RESPOND_ERROR, 'Not in recursive-mode, try again with -r'); continue; } $newDirectory = $subDirectory . $Name . '/'; $newPath = $baseDirectory . $newDirectory; // Try to create the directory if not existant if (!is_dir ($newPath)) { if (!mkdir ($newPath, $Mode)) { respond (RESPOND_ERROR, 'Could not create the desired directory'); continue; } // Preserve directory-modes } elseif ($Preserve) @chmod ($newDirectory, $Mode); $subDirectory = $newDirectory; fwrite (STDERR, 'New subdir: ' . $subDirectory . "\n"); respond (); continue; } // Try to open the destination $File = $baseDirectory . $subDirectory . $Name; if (!is_resource ($f = @fopen ($File, 'w'))) { respond (RESPOND_ERROR, 'Could not open destination'); continue; } // Request file-contents respond (); $Read = 0; while (($Pending = $Size - $Read) > 0) { $buf = fread (STDIN, $Pending); if ($buf === false) { respond (RESPOND_ERROR, 'Could not read'); return false; } fwrite ($f, $buf); $Read += strlen ($buf); } fclose ($f); if ($Preserve) { chmod ($File, $Mode); touch ($File, $mTime, $aTime); } readResponse (); respond (); break; default: respond (RESPOND_ERROR, 'Invalid Request ' . ord ($Action)); return false; } } return true; } // }}} // {{{ submit /** * Send files from local system over the wire * * @param array $argv Array containing the requested files/directories * * @access public * @return bool **/ function submit ($argv) { // Wait until the receiver is ready if (readResponse () != RESPOND_OK) return false; global $Preserve, $Recursive; // Handle all requested files foreach ($argv as $File) { // Check if requested path is a file if (is_file ($File)) fsubmit ($File, $Preserve); // Check if requested path is a directory elseif (is_dir ($File)) { if (!$Recursive) respond (RESPOND_ERROR, 'Is a directory, try accessing in recursive mode: ' . $File); else dsubmit ($File, $Preserve); } } return true; } // }}} // {{{ fsubmit /** * Submit a single file over the wire * * @param string $File Path to file * @param bool $Preserve (optional) Preserve atime and mtime of file * * @access private * @return bool **/ function fsubmit ($File, $Preserve = false) { fwrite (STDERR, 'Submit file ' . $File . "\n"); // Try to open the source if (!is_resource ($f = @fopen ($File, 'r'))) { respond (RESPOND_ERROR, 'Could not open ' . $File); return false; } // Send file-times if requested if ($Preserve) { fwrite (STDOUT, 'T' . filemtime ($File) . ' 0 ' . fileatime ($File) . ' 0' . "\n"); if (readResponse () != RESPOND_OK) return false; } // Send out fileperms, -mode and -name fwrite (STDOUT, 'C' . substr (decoct (fileperms ($File)), -4) . ' ' . filesize ($File) . ' ' . basename ($File) . "\n"); if (readResponse () != RESPOND_OK) return false; // Send the contents while (!feof ($f)) fwrite (STDOUT, fread ($f, 4096)); fclose ($f); respond (); return (readResponse () == RESPOND_OK); } // }}} // {{{ dsubmit /** * Send an entire directory over the wire * * @param string $Directory Path to directory * @param bool $Preserve (optional) Preserve filetimes * * @access private * @return bool **/ function dsubmit ($Directory, $Preserve = false) { fwrite (STDERR, 'Submit directory ' . $Directory . "\n"); // Enter this directory fwrite (STDOUT, 'D' . substr (decoct (fileperms ($Directory)), -4) . ' 0 ' . basename ($Directory) . "\n"); if (readResponse () != RESPOND_OK) return false; if ($Directory [strlen ($Directory) - 1] != '/') $Directory .= '/'; // Access contents of directory if (!is_object ($D = @dir ($Directory))) { respond (RESPOND_ERROR, 'Could not list directory-contents: ' . $Directory); return false; } while ($f = $D->read ()) { // Skip special directories if (($f == '..') || ($f == '.')) continue; if (is_file ($Directory . $f)) fsubmit ($Directory . $f, $Preserve); else dsubmit ($Directory . $f, $Preserve); } $D->close (); // Leave our directory fwrite (STDOUT, 'E' . "\n"); return (readResponse () != RESPOND_OK); } // }}} // Handle parameters $Role = null; $Verbose = false; $Recursive = false; $RequireDirectory = false; $Preserve = false; $Command = array_shift ($argv); while (count ($argv) > 0) { $opt = array_shift ($argv); switch ($opt) { case '-t': $Role = ROLE_RECEIVER; break; case '-f': $Role = ROLE_SUBMITTER; break; case '-d': $RequireDirectory = true; break; case '-r': $Recursive = true; break; case '-v': $Verbose = true; break; case '-p': $Preserve = true; break; case '--': break (2); default: if ($opt [0] == '-') exit (1); array_unshift ($argv, $opt); break (2); } } // Forward to handlers if ($rc = ($Role == ROLE_SUBMITTER)) $rc = submit ($argv); elseif ($Role == ROLE_RECEIVER) $rc = receive ($argv); exit ($rc ? 0 : 1); ?>