I've been trying to follow this tutorial and I did everything it said. I clicked the download button, installed XAMPP, copied the directory of the only server.php file i found, and put it in the following line, and ran it in the shell like it said.
php -q c:\xampp1\php\pear\adodb\server.php
But I got a bunch of warnings about files not being included and other stuff, which i don't understand because I downloaded everything it said and followed the tutorial. This is a screenshot of all my errors (I can't copy paste in xampp shell):
EDIT: Here is the server.php files code:
<?php
/**
* #version V4.93 10 Oct 2006 (c) 2000-2012 John Lim (jlim#natsoft.com). All rights reserved.
* Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
the BSD license will take precedence.
*/
/* Documentation on usage is at http://php.weblogs.com/adodb_csv
*
* Legal query string parameters:
*
* sql = holds sql string
* nrows = number of rows to return
* offset = skip offset rows of data
* fetch = $ADODB_FETCH_MODE
*
* example:
*
* http://localhost/php/server.php?select+*+from+table&nrows=10&offset=2
*/
/*
* Define the IP address you want to accept requests from
* as a security measure. If blank we accept anyone promisciously!
*/
$ACCEPTIP = '127.0.0.1';
/*
* Connection parameters
*/
$driver = 'mysql';
$host = 'localhost'; // DSN for odbc
$uid = 'root';
$pwd = 'garbase-it-is';
$database = 'test';
/*============================ DO NOT MODIFY BELOW HERE =================================*/
// $sep must match csv2rs() in adodb.inc.php
$sep = ' :::: ';
include('./adodb.inc.php');
include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
function err($s)
{
die('**** '.$s.' ');
}
// undo stupid magic quotes
function undomq(&$m)
{
if (get_magic_quotes_gpc()) {
// undo the damage
$m = str_replace('\\\\','\\',$m);
$m = str_replace('\"','"',$m);
$m = str_replace('\\\'','\'',$m);
}
return $m;
}
///////////////////////////////////////// DEFINITIONS
$remote = $_SERVER["REMOTE_ADDR"];
if (!empty($ACCEPTIP))
if ($remote != '127.0.0.1' && $remote != $ACCEPTIP)
err("Unauthorised client: '$remote'");
if (empty($_REQUEST['sql'])) err('No SQL');
$conn = ADONewConnection($driver);
if (!$conn->Connect($host,$uid,$pwd,$database)) err($conn->ErrorNo(). $sep . $conn->ErrorMsg());
$sql = undomq($_REQUEST['sql']);
if (isset($_REQUEST['fetch']))
$ADODB_FETCH_MODE = $_REQUEST['fetch'];
if (isset($_REQUEST['nrows'])) {
$nrows = $_REQUEST['nrows'];
$offset = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : -1;
$rs = $conn->SelectLimit($sql,$nrows,$offset);
} else
$rs = $conn->Execute($sql);
if ($rs){
//$rs->timeToLive = 1;
echo _rs2serialize($rs,$conn,$sql);
$rs->Close();
} else
err($conn->ErrorNo(). $sep .$conn->ErrorMsg());
?>
I was going to google the errors but I think they are self explanatory, I just don't know why this code would give me includes when they aren't there and why it would give me parameters that aren't defined as in the final notice at the bottom of the screen cap. I'm guessing I have to put an IP address somewhere but I'm not sure where and if that's all. BTW I have a mysql database I can log into, I just don't know how to combine it with this.
How do I proceed in following this tutorial and get a running chat working?
(edit: I updated the screencap to reflect adding the 1 to xampp in the line but its mostly the same)
Because you're running PHP from the command line, there is no value in $_SERVER["REMOTE_ADDR"]. You should change that line to:
$remote = isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '127.0.0.1';
or if you're using PHP7
$remote = $_SERVER["REMOTE_ADDR"] ?? '127.0.0.1';
Related
I have a php class/function that allow me to perform a query on the DB and then return an associative array of the result.
Everything works perfectly fine on my local development environment (MAMP).
Once I moved the site on a godaddy Linux Shared Hosting I get the following error: Fatal error: Call to undefined method mysqli_stmt::get_result() in /home/username/public_html/dev.x-matcher.com/admin/classes/c_RequeteSQL.php on line 146
I have followed the following article(https://ca.godaddy.com/help/enable-custom-php-modules-12036) in order to change the PHP version from 5.4 native to 5.4 and therefore enable the mysqlnd custom extension.
Godaddy CPANEL - Select PHP Version
Once activated the error disapears but the script still blocks at the same place, so still not working.
I have also tried to keep the 5.4 native version of PHP and upload a custom php.ini file in which I added the following line : extension=php_mysqli_mysqlnd.so and still not working...
I have read a lot of thread like this one(stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result) but could'nt find any solution to my problem.
I understand that I could use bind_param and fetch to counter the problem but I do not want to do so as My function need to dynamically fetch the results in an associative array and then return the array. Using bind_param forces my to enter the variables and since the number of returned variables are never the same depending on the request it not the solution I am looking for..
You can see my phpinfo if you follow this link: dev.x-matcher.com/phpinfo.php
As explained my whole coding works 100% on my local environment but here is my class just in case:
class RequeteSQL {
private $dbhost = 'localhost';
private $dbuser = 'user';
private $dbpass = 'pass';
function retourArray($requete,$lettres,$parametres){
global $dbhost;
global $dbuser;
global $dbpass;
$basededon = "db";
$connection = new mysqli($dbhost,$dbuser,$dbpass,$basededon);
$statement = $connection->prepare($requete);
if ($statement === false) {
trigger_error('Probleme avec la requete SQL :' . $requete . ' Erreur code : ' . $connection->error, E_USER_ERROR);
} else {
if ($parametres!=NULL) {
if (is_array($parametres)) {
$a_params = array();
$param_type = '';
$n = count($lettres);
for ($i = 0; $i < $n; $i++) {
$param_type .= $lettres[$i];
}
$a_params[] = & $param_type;
for ($i = 0; $i < $n; $i++) {
$a_params[] = & $parametres[$i];
}
call_user_func_array(array($statement, 'bind_param'), $a_params);
} else {
$statement->bind_param($lettres,$parametres);
}
}
$statement->execute();
$resultat = $statement->get_result();
$resultarray = $resultat->fetch_all(MYSQLI_ASSOC);
$statement->close();
$connection->close();
return $resultarray;
}
}
function __destruct(){
}
}
What am I doing wrong on the server so php_mysqli_mysqlnd is not working OR is there a hosting service that DO supports this extension?
Since I couldn't find an answer I decided to change my code to use PDO instead of mysqli. It worked perfectly. I can't send you to a proper tutorial since I speak french and read mine in french. But a lot of resources are out there to use the PDO. It is pretty ease. Hope this helps.
OK, this is a big problem of mine and I've spent over an hour creating a working example so I'm hoping someone out there can sympathise enough with my problem enough to give some help or feedback.
I am prepared to give you bash access to a centos system running php 5.4 so you can test the code yourself and I'll even invite you into the telegram group "test chat" so you can see results for yourself. If you accept donations I'm prepared to donate $$$$$$ just name your price.
Here's scenario:
I'm running a communications bot that connects to irc and telegram. The idea is people type !uptime in the chat channel (#public on irc.glx-alliance.com:6668) and in Telegram group test chat and the bot tells them how long they have been online for. The example is working, both IRC and TG return uptime stats upon request through !uptime command. So far so good.
Here's the problem:
When i type !uptime in the IRC client, i get a super fast 0.02 seconds response time. Excellent. That works. However for the Telegram integration, !uptime can take up to 30 seconds to respond. The reason why is below.
Some more details:
The initiator for Telegram responding is not that the loop is checking very slowly, but that IRC has sent data to the IRC connection. Which then prompts the telegram code to run. I can reproduce this easily by simply typing into the chat channel and immediately the Telegram test chat channel receives the uptime response.
Here's my working example. For this to work, you will need to open port 6668 in your firewall, have telegram-cli installed and be running it from a directory with telegram-cli-php installed. See https://github.com/zyberspace/php-telegram-cli-client. Simply type composer require zyberspace/telegram-cli-client in the project directory and crete a telegramIntegrations.php file in an includes directory with the call to vendor.php in there. (require('vendor/autoload.php');). My phone number is +447935499706 add to me telegram and I'll invite you to the test chat group.
Here's the code. Short of playing code golf I can't get the file size down any further. I will describe the important bits after the code itself.
<?php
set_time_limit(0);
ini_set('display_errors', 'on');
global $configure;
$configure = array(
'server' => 'irc.glx-alliance.com',
'port' => 6668,
'nick' => 'ExampleBot',
'name' => 'Example Bot'
);
include_once('../includes/telegramIntegration.php');
class IRCBot{
// TCP connection holder.
public $socket;
// Message holder.
public $msg = array();
/*
* Constucter.
* Opens the server connection, and logs in the bot.
*
* #param array.
*/
function __construct($configure){
echo '-----------Socket opening...----------------------------------' ."\r\n";
$this->socket = fsockopen($configure['server'], $configure['port']);
$this->login($configure);
$this->timestamp = time();
$this->main();
}
/*
* Logs bot in to server
*
* #param array.
*/
function login ($configure){
$this->send_data('USER', $configure['nick'] . ' rogues-alliance.com ' . $configure['nick'] . ' :' . $configure['name']);
$this->send_data('NICK', $configure['nick']);
}
/*
* Startup commands
*/
function startup () {
echo 'Startup initiated...' . PHP_EOL;
echo 'Startup finished' . PHP_EOL;
}
/*
* Bot Command
*/
function intel () {
return $this->intel;
}
/*
* Main function, used to grab all data.
*/
function main(){
while (true):
/* Fetch Data From Telegram Socket */
$this->telegram = new \Zyberspace\Telegram\Cli\Client('unix:///tmp/tg.sck');
/* Fetch Data From IRC Socket */
$data = fgets($this->socket, 1024);
flush();
$data = preg_replace('/\s{2,}/ ', ' ', $data) . PHP_EOL;
$this->ex = explode(' ', $data);
/* Ping Pong */
if($this->ex[0] == 'PING'):
$this->send_data('PONG', $this->ex[1]);
endif;
/* Internal While Loops */
if (!$this->firstRun) {
$this->firstRun = true;
// do some stuff
}
if ($this->inited){ // have we had the server motd etc
}
/* Format Text */
$command = str_replace(array(chr(10), chr(13)), '', $this->ex[3]);
if (strtoupper($this->ex[1]) == $this->ex[1]):
$request = $this->ex[1];
endif;
/* Handle Text from IRC $data */
switch ($request):
case 'PRIVMSG':
/* Setup Variables */
$host = $this->ex[0];
$username = substr($host, 1, strpos($host, '!')-1);
$target = $this->ex[2];
// list of commands the bot responds to
switch ($command){
case ':!uptime':
$this->sendMessage($this->uptime());
break;
break;
case ':!help':
$this->sendMessage('Available commands: !uptime', $this->ex[2]);
break;
}
break;
case '372':
case '375':
case '265':
case '255':
case '254':
case '003':
case '002':
echo $text;
break;
case '376':
/* Startup Commands */
$this->startup();
break;
case 'QUIT':
break;
default:
endswitch;
/* Handle Text From Telegram $telegram */
if (!$channels)
$channels = array(
'test chat'
);
foreach ($channels as $channel):
if (!$this->_tgData[$channel]):
$this->_tgData[$channel] = $this->telegram->getHistory($channel, 1);
$this->_tgData[$channel] = substr($this->_tgData[$channel], strpos($this->_tgData[$channel], ']')+3);
endif;
// fetch data
$this->_history[$channel] = $this->telegram->getHistory($channel, 1, 0);
$this->_history[$channel] = substr($this->_history[$channel], strpos($this->_history[$channel], ']')+3);
flush();
$b=0;
$output = array();
while (str_replace('>>>' , '', str_replace('»»»', '', str_replace('«««', '', str_replace('<<<', '', $this->_tgData[$channel])))) != str_replace('>>>' , '', str_replace('»»»', '', str_replace('«««', '', str_replace('<<<', '', $this->_history[$channel]))))):
// fetch data
$this->_history[$channel] = $this->telegram->getHistory($channel, 1, $b);
$this->_history[$channel] = substr($this->_history[$channel], strpos($this->_history[$channel], ']')+3);
flush();
if (preg_match("/(.+) [«><»]{3} (![\w\d]+) (.+)/", $this->_history[$channel], $matches)):
$username = substr(str_replace($channel, '', $matches[1]), 1);
$command = $matches[2];
$tokens = explode(' ', $matches[3]);
switch($command):
case '!uptime':
echo 'got here';
$this->telegram->msg($channel, $this->uptime());
endswitch;
endif;
$b++;
endwhile;
endforeach;
endwhile;
}
function sendMessage ($message, $to = false){
$this->send_data("PRIVMSG", (!$to?$this->ex[2]:$to) . " :" . $message);
}
/*
* Sends data to the server.
*/
function send_data($cmd, $msg = null){
if($msg == null){
fputs($this->socket, $cmd . "\n");
} else {
fputs($this->socket, $cmd.' '.$msg."\n");
}
}
function uptime () {
echo '------time-----';
$days = round((time() - $this->timestamp)/60/60/24);
$hours = round((time() - $this->timestamp)/60/60%24);
$minutes = round((time() - $this->timestamp)/60%60);
echo $this->timestamp;
echo '---time----';
return "I have been online for $days days, $hours hours and $minutes minutes";
}
}
$bot = new IRCBot($configure);
?>
So the important parts of the code are as follows:
while (true): /*code*/ endwhile;
/* Fetch Data From Telegram Socket */
$this->telegram = new \Zyberspace\Telegram\Cli\Client('unix:///tmp/tg.sck');
/* Fetch Data From IRC Socket */
$data = fgets($this->socket, 1024);
flush();
Data is fetched from IRC socket and Telegram socket, but the IRC socket is receiving data at this point, telegram has to make an additional call to receive data. See below.
$this->_tgData[$channel] = $this->telegram->getHistory($channel, 1);
$this->_tgData[$channel] = substr($this->_tgData[$channel], strpos($this->_tgData[$channel], ']')+3);
Telegram picks its data up here.
So that covers everything I can think of. I will be using the "show this question to a friend" feature so please no posts about how it's too much code; I have explained the relevant parts so please go easy. Also, the code could not be any shorter if I tried. This is a complete example case and I'm prepared to offer you an environment to test within, for which I will donate to you or a charity of your choosing.
Also please note: can someone create the tags telegram-cli and telegram-cli-php, both are very useful projects to the community.
So in the end the result was to change MySQL session lifetime to something more apt.
today one of my friends had a problem with his guestbook. We use a small php orientated guestbook which was working fine except for one thing: it had reached its limit of messages.
So what i did is edit the blog file and change the following setting:
//Maximum entry stored in data file
$max_record_in_data_file = 1800;
The moment I did this though, something went very wrong. I uploaded the file back on the server and got the following:
Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at E:\inetpub\vhosts\trilogianocturnus.com\httpdocs\guestbook.php:1) in E:\inetpub\vhosts\trilogianocturnus.com\httpdocs\guestbook.php on line 95
I don't know what this is, I'm very new to php, but from what I understand, it means something is already being called by the browser before session_start
The page is located at:
http://trilogianocturnus.com/guestbook.php
The code before the head is as follows:
<?
/*-----------------------------------------------------
COPYRIGHT NOTICE
Copyright (c) 2001 - 2008, Ketut Aryadana
All Rights Reserved
Script name : ArdGuest
Version : 1.8
Website : http://www.promosi-web.com/script/guestbook/
Email : aryasmail#yahoo.com.au
Download URL :
- http://www.promosi-web.com/script/guestbook/download/
- http://www.9sites.net/download/ardguest_1.8.zip
This code is provided As Is with no warranty expressed or implied.
I am not liable for anything that results from your use of this code.
------------------------------------------------------*/
//--Change the following variables
//Title of your guestbook
$title = "Guestbook Nocturnus";
//Change "admin" with your own password. It's required when you delete an entry
$admin_password = "***";
//Enter your email here
$admin_email = "***";
//Your website URL
$home = "http://www.trilogianocturnus.com/main.html";
//Send you an email when someone add your guestbook, YES or NO
$notify = "YES";
//Your Operating System
//For Windows/NT user : WIN
//For Linux/Unix user : UNIX
$os = "WIN";
//Maximum entry per page when you view your guestbook
$max_entry_per_page = 10;
//Name of file used to store your entry, change it if necessary
$data_file = "ardgb18.dat";
//Maximum entry stored in data file
$max_record_in_data_file = 1800;
//Maximum entries allowed per session, to prevent multiple entries made by one visitor
$max_entry_per_session = 10;
//Enable Image verification code, set the value to NO if your web server doesn't support GD lib
$imgcode = "YES";
//Color & font setting
$background = "#000";
$table_top = "#000";
$table_content_1a = "#090909";
$table_content_1b = "#000000";
$table_content_2a = "#090909";
$table_content_2b = "#000000";
$table_bottom = "#000";
$table_border = "#1f1f1f";
$title_color = "#9f0000";
$link = "#9f0000";
$visited_link = "#9f0000";
$active_link = "#9f0000";
$font_face = "verdana";
$message_font_face = "arial";
$message_font_size = "2";
//-- Don't change bellow this line unless you know what you're doing
$do = isset($_REQUEST['do']) ? trim($_REQUEST['do']) : "";
$id = isset($_GET['id']) ? trim($_GET['id']) : "";
$page = isset($_GET['page']) ? $_GET['page'] : 1;
$self = $_SERVER['PHP_SELF'];
if (!file_exists($data_file)) {
echo "<b>Error !!</b> Can't find data file : $data_file.<br>";
exit;
} else {
if ($max_record_in_data_file != "0") {
$f = file($data_file);
rsort($f);
$j = count($f);
if ($j > $max_record_in_data_file) {
$rf = fopen($data_file,"w");
if (strtoupper($os) == "UNIX") {
if (flock($rf,LOCK_EX)) {
for ($i=0; $i<$max_record_in_data_file; $i++) {
fwrite($rf,$f[$i]);
}
flock($rf,LOCK_UN);
}
} else {
for ($i=0; $i<$max_record_in_data_file; $i++) {
fwrite($rf,$f[$i]);
}
}
fclose($rf);
}
}
}
session_start();
$newline = (strtoupper($os) == "WIN") ? "\r\n" : "\n";
switch ($do) {
case "":
$record = file($data_file);
rsort($record);
$jmlrec = count($record);
?>
I have of course, removed the password and email for security, now here isthe funny part.
This error started happening the moment i changed that setting up up there, but if i tried to revert it back to 1800 (i changed it to 11800 to test it out), it still gives me that error.
Any idea of what this is?
The guestbook url is: promosi-web.com/script/guestbook/
The most common cause of this error is something being added to the file before the <?
Most likely a space or UTF byte order mark.
Put your session_start() after <? and you should be fine
Note:
To use cookie-based sessions, session_start() must be called before outputing anything to the browser.
http://php.net/manual/en/function.session-start.php
The message says that the “output started at …\guestbook.php:1”. So there must be something in that file on that line that initiated the output.
Make sure that there are no whitespace or other invisible characters (like a BOM) before the opening <? tag.
Check if you have a space or a byte order mark, you can also do an
ob_start(); at the beginning of the page and ob_end_flush(); at the end to solve this issue.
but IMO check for the space or the B.O.M
I'm writing a command line tool to help my web app. It needs a password to connect to the service. I'd like the script to show a password prompt so I don't have to pass it as a command line argument.
That's easy enough, but I'd like it to not echo the password to the screen as it's typed. How can I do this with PHP?
Bonus points for doing it in pure PHP (no system('stty')) and replacing the characters with *.
EDIT:
The script will run on a unix like system (linux or mac). The script is written in PHP, and will most likely stay like that.
Also, for the record, the stty way of doing it is:
echo "Password: ";
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
// add a new line since the users CR didn't echo
echo "\n";
I'd prefer to not have the system() calls in there.
Found on sitepoint.
function prompt_silent($prompt = "Enter Password:") {
if (preg_match('/^win/i', PHP_OS)) {
$vbscript = sys_get_temp_dir() . 'prompt_password.vbs';
file_put_contents(
$vbscript, 'wscript.echo(InputBox("'
. addslashes($prompt)
. '", "", "password here"))');
$command = "cscript //nologo " . escapeshellarg($vbscript);
$password = rtrim(shell_exec($command));
unlink($vbscript);
return $password;
} else {
$command = "/usr/bin/env bash -c 'echo OK'";
if (rtrim(shell_exec($command)) !== 'OK') {
trigger_error("Can't invoke bash");
return;
}
$command = "/usr/bin/env bash -c 'read -s -p \""
. addslashes($prompt)
. "\" mypassword && echo \$mypassword'";
$password = rtrim(shell_exec($command));
echo "\n";
return $password;
}
}
Depending on your environment (i.e., not on Windows), you can use the ncurses library (specifically, the ncurses_noecho() function to stop keyboard echo and ncurses_getch() to read the input) to get the password without displaying it on screen.
You can use my hiddeninput.exe file to get real hidden input without leaking the information anywhere on screen.
<?php
echo 'Enter password: ';
$password = exec('hiddeninput.exe');
echo PHP_EOL;
echo 'Password was: ' . $password . PHP_EOL;
If you remove the last echo, the password should never show up, but you can use that for validation obvoiusly.
The below method works under Linux CLI but not under Windows CLI or Apache. It also only works with chars in the standard Ascii table (It would not take much to make it compatible with extended char sets though).
I have put a bit of code in to protect against copy and paste passwords. If the bit between the two comments is removed then a password can be injected/pasted in.
I hope this helps someone.
<?php
echo("Password: ");
$strPassword=getObscuredText();
echo("\n");
echo("You entered: ".$strPassword."\n");
function getObscuredText($strMaskChar='*')
{
if(!is_string($strMaskChar) || $strMaskChar=='')
{
$strMaskChar='*';
}
$strMaskChar=substr($strMaskChar,0,1);
readline_callback_handler_install('', function(){});
$strObscured='';
while(true)
{
$strChar = stream_get_contents(STDIN, 1);
$intCount=0;
// Protect against copy and paste passwords
// Comment \/\/\/ to remove password injection protection
$arrRead = array(STDIN);
$arrWrite = NULL;
$arrExcept = NULL;
while (stream_select($arrRead, $arrWrite, $arrExcept, 0,0) && in_array(STDIN, $arrRead))
{
stream_get_contents(STDIN, 1);
$intCount++;
}
// /\/\/\
// End of protection against copy and paste passwords
if($strChar===chr(10))
{
break;
}
if ($intCount===0)
{
if(ord($strChar)===127)
{
if(strlen($strObscured)>0)
{
$strObscured=substr($strObscured,0,strlen($strObscured)-1);
echo(chr(27).chr(91)."D"." ".chr(27).chr(91)."D");
}
}
elseif ($strChar>=' ')
{
$strObscured.=$strChar;
echo($strMaskChar);
//echo(ord($strChar));
}
}
}
readline_callback_handler_remove();
return($strObscured);
}
?>
This is the easiest solution for all platforms:
function prompt($message = 'prompt: ', $hidden = false) {
if (PHP_SAPI !== 'cli') {
return false;
}
echo $message;
$ret =
$hidden
? exec(
PHP_OS === 'WINNT' || PHP_OS === 'WIN32'
? __DIR__ . '\prompt_win.bat'
: 'read -s PW; echo $PW'
)
: rtrim(fgets(STDIN), PHP_EOL)
;
if ($hidden) {
echo PHP_EOL;
}
return $ret;
}
Then create prompt_win.bat in the same directory:
SetLocal DisableDelayedExpansion
Set "Line="
For /F %%# In ('"Prompt;$H & For %%# in (1) Do Rem"') Do (
Set "BS=%%#"
)
:loop_start
Set "Key="
For /F "delims=" %%# In ('Xcopy /L /W "%~f0" "%~f0" 2^>Nul') Do (
If Not Defined Key (
Set "Key=%%#"
)
)
Set "Key=%Key:~-1%"
SetLocal EnableDelayedExpansion
If Not Defined Key (
Goto :loop_end
)
If %BS%==^%Key% (
Set "Key="
If Defined Line (
Set "Line=!Line:~0,-1!"
)
)
If Not Defined Line (
EndLocal
Set "Line=%Key%"
) Else (
For /F "delims=" %%# In ("!Line!") Do (
EndLocal
Set "Line=%%#%Key%"
)
)
Goto :loop_start
:loop_end
Echo;!Line!
I guess that there is no simple way of doing it (actually I can't think of any way) without using stty -echo.
If you intent running it on windows, you could create a batch script that would provide the unechoed typed info to your php script.
#echo off
cls
SET /P uname=Enter Username:
echo hP1X500P[PZBBBfh#b##fXf-V#`$fPf]f3/f1/5++u5>in.com
set /p password=Enter password :<nul
for /f “tokens=*” %%i in (’in.com’) do set password=%%i
del in.com
echo.
c:\php\php.exe d:\php\test.php %uname% “%password%”
Pause
example taken from http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/
Works on every windows system, that has powershell support. (source from: http://www.qxs.ch/2013/02/08/php-cli-password-prompts-on-windows-7/ )
<?php
// please set the path to your powershell, here it is: C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe
$pwd=shell_exec('C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -Command "$Password=Read-Host -assecurestring \"Please enter your password\" ; $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) ; echo $PlainPassword;"');
$pwd=explode("\n", $pwd); $pwd=$pwd[0];
echo "You have entered the following password: $pwd\n";
system('stty -echo');
to disable the current terminal echo, and:
system('stty echo');
to reenable it. Set it before and after fgets.
The accepted answer is not good enough. First of all, the Windows solution doesn't work on Windows 7 and above. The solution for other OSs depends on Bash and bash built-in 'read'. However, there are systems which does not use Bash (eg. OpenBSD) and where this obviously won't work.
In this blog I've discussed solution which works on almost any Unix based OS and Windows from 95 to 8. The Windows solution uses external program written in C on top Win32 API. The solution for other OSs uses external command 'stty'. I have yet to see a Unix based system which does not have 'stty'
Why not use an SSH connection? You can abstract the commands away, redirect input/output and have full control.
You can provide someone with a pure clean shell with as little rights as neccesary, and let the password just be POST'ed along with to SSH2::Connect() to open the shell.
I created a nice class to work with the php SSH2 extension, maybe it helps you;
(and it also does secure file transfers)
<?php
/**
* SSH2
*
* #package Pork
* #author SchizoDuckie
* #version 1.0
* #access public
*/
class SSH2
{
private $host;
private $port;
private $connection;
private $timeout;
private $debugMode;
private $debugPointer;
public $connected;
public $error;
/**
* SSH2::__construct()
*
* #param mixed $host
* #param integer $port
* #param integer $timeout
* #return
*/
function __construct($host, $port=22, $timeout=10)
{
$this->host = $host;
$this->port = $port;
$this->timeout = 10;
$this->error = 'not connected';
$this->connection = false;
$this->debugMode = Settings::Load()->->get('Debug', 'Debugmode');
$this->debugPointer = ($this->debugMode) ? fopen('./logs/'.date('Y-m-d--H-i-s').'.log', 'w+') : false;
$this->connected = false;
}
/**
* SSH2::connect()
*
* #param mixed $username
* #param mixed $password
* #return
*/
function connect($username, $password)
{
$this->connection = ssh2_connect($this->host, $this->port);
if (!$this->connection) return $this->error("Could not connect to {$this->host}:{$this->port}");
$this->debug("Connected to {$this->host}:{$this->port}");
$authenticated = ssh2_auth_password($this->connection, $username, $password);
if(!$authenticated) return $this->error("Could not authenticate: {$username}, check your password");
$this->debug("Authenticated successfully as {$username}");
$this->connected = true;
return true;
}
/**
* SSH2::exec()
*
* #param mixed $command shell command to execute
* #param bool $onAvailableFunction a function to handle any available data.
* #param bool $blocking blocking or non-blocking mode. This 'hangs' php execution until the command has completed if you set it to true. If you just want to start an import and go on, use this icm onAvailableFunction and false
* #return
*/
function exec($command, $onAvailableFunction=false, $blocking=true)
{
$output = '';
$stream = ssh2_exec($this->connection, $command);
$this->debug("Exec: {$command}");
if($onAvailableFunction !== false)
{
$lastReceived = time();
$timeout =false;
while (!feof($stream) && !$timeout)
{
$input = fgets($stream, 1024);
if(strlen($input) >0)
{
call_user_func($onAvailableFunction, $input);
$this->debug($input);
$lastReceived = time();
}
else
{
if(time() - $lastReceived >= $this->timeout)
{
$timeout = true;
$this->error('Connection timed out');
return($this->error);
}
}
}
}
if($blocking === true && $onAvailableFunction === false)
{
stream_set_blocking($stream, true);
$output = stream_get_contents($stream);
$this->debug($output);
}
fclose($stream);
return($output);
}
/**
* SSH2::createDirectory()
*
* Creates a directory via sftp
*
* #param string $dirname
* #return boolean success
*
*/
function createDirectory($dirname)
{
$ftpconnection = ssh2_sftp ($this->connection);
$dircreated = ssh2_sftp_mkdir($ftpconnection, $dirname, true);
if(!$dircreated)
{
$this->debug("Directory not created: ".$dirname);
}
return $dircreated;
}
public function listFiles($dirname)
{
$input = $this->exec(escapeshellcmd("ls {$dirname}"));
return(explode("\n", trim($input)));
}
public function sendFile($filename, $remotename)
{
$this->debug("sending {$filename} to {$remotename} ");
if(file_exists($filename) && is_readable($filename))
{
$result = ssh2_scp_send($this->connection, $filename, $remotename, 0664);
}
else
{
$this->debug("Unable to read file : ".$filename);
return false;
}
if(!$result) $this->debug("Failure uploading {$filename} to {$remotename}");
return $result;
}
public function getFile($remotename, $localfile)
{
$this->debug("grabbing {$remotename} to {$localfile}");
$result = ssh2_scp_recv($this->connection, $remotename, $localfile);
if(!$result) $this->debug("Failure downloading {$remotename} to {$localfile}");
return $result;
}
/**
* SSH2::debug()
*
* #param mixed $message
* #return
*/
function debug($message)
{
if($this->debugMode)
{
fwrite($this->debugPointer, date('Y-m-d H:i:s')." : ".$message."\n");
}
}
/**
* SSH2::error()
*
* #param mixed $errorMsg
* #return
*/
function error($errorMsg)
{
$this->error = $errorMsg;
$this->debug($errorMsg);
return false;
}
/**
* SSH2::__destruct()
*
* #return
*/
function __destruct()
{
if($this->connection){
$this->connection = null;
}
if($this->debugMode && $this->debugPointer)
{
fclose($this->debugPointer);
}
}
}
Usage example:
$settings = Settings::Load()->Get("SecureServer");
$ssh = new SSH2($settings['host']);
if( $ssh->connect($settings['username'], $settings['password']))
{
echo $ssh->exec("ls -la ".$settings['path'], false, true);
flush();
}
Theorically you can do it using stream_set_blocking(), but looks like there are some PHP bugs managing STDIN.
Look:
http://bugs.php.net/bug.php?id=34972
http://bugs.php.net/bug.php?id=36030
Try yourself:
echo "Enter Password: ";
$stdin = fopen('php://stdin','r');
// Trying to disable stream blocking
stream_set_blocking($stdin, FALSE) or die ('Failed to disable stdin blocking');
// Trying to set stream timeout to 1sec
stream_set_timeout ($stdin, 1) or die ('Failed to enable stdin timeout');
I am working on a script that downloads emails and stores them in a db, I usually receive thousands of emails on this account, once downloaded the mails are deleted.
Being paranoic, I want to have at least one month backup of my emails, but I cannot clutter my main mailbox address leaving them in there.
So i need to move the mails (via php code) from one mailbox to another. I came up with this solution that uses imap_append(). This solution, however recreates the email, and does not really move it.
Do you have any suggestions or alternative ways of doing this?
Remember: it must be done in php, because I need to integrate it in my readmail script.
I have already seen this thread where a fetchmail solution was proposed
Here follows the code I wrote for this task
<?php
/**
* Conn params
*/
$fromMboxServerPath = "{imap.from.server/notls/imap:143}";
$fromMboxMailboxPath = "INBOX";
$fromMboxMailAddress = "login";
$fromMboxMailPass = "pass";
$toMboxServerPath = "{imap.to.server/notls/imap:143}";
$toMboxMailboxPath = "INBOX";
$toMboxMailAddress = "login";
$toMboxMailPass = "pass";
$fromMboxConnStr = $fromMboxServerPath.$fromMboxMailboxPath;
$toMboxConnStr = $toMboxServerPath.$toMboxMailboxPath;
$fetchStartSeq = 1;
$fetchEndSeq = 10;
function myLog($str)
{
echo "Log [".date('Y-m-d H:i:s')."]: $str\n";
}
myLog("Connecting to mailbox");
function mboxConn($connstr,$addr,$pass)
{
if(!($mbox = #imap_open($connstr, $addr, $pass)))
{
myLog("Error: ".imap_last_error());
die;
}
else
{
myLog("Connected to: $addr $connstr");
return $mbox;
}
}
function mboxCheck($mbox)
{
if(!($mbox_data = imap_check($mbox)))
{
myLog("Error: ".imap_last_error());
die;
}
else
{
myLog("Mailbox check ".$mbox_data->Mailbox." OK");
myLog($mbox_data->Nmsgs." messages present");
return $mbox_data->Nmsgs;
}
}
$fromMbox = mboxConn($fromMboxConnStr, $fromMboxMailAddress, $fromMboxMailPass);
$toMbox = mboxConn($toMboxConnStr, $toMboxMailAddress, $toMboxMailPass);
$fromMboxCount = mboxCheck($fromMbox);
$toMboxCount = mboxCheck($toMbox);
/**
* Loop on mails
*/
$fetchStartUID = imap_uid($fromMbox,$fetchStartSeq);
if ($fromMboxCount < $fetchEndSeq)
{
$fetchEndSeq = $fromMboxCount;
}
$fetchEndUID = imap_uid($fromMbox,$fetchEndSeq);
/**
* Loop on mails
*/
myLog("Do stuff and backup from UID [$fetchStartUID] to UID [$fetchEndUID]");
for ($i=$fetchStartSeq;$i<=$fetchEndSeq;$i++)
{
$pfx = "Msg #$i : ";
$h = imap_header($fromMbox, $i);
$fh = imap_fetchheader($fromMbox, $i);
$fb = imap_body($fromMbox, $i);
$message = $fh.$fb;
$msgUID = imap_uid($fromMbox,$i);
$struct = imap_fetchstructure ($fromMbox, $i);
/**
* We do some logging
*/
myLog($pfx."UID [".$msgUID."] SEQ [".imap_msgno($fromMbox,$msgUID)."] Flags: [". $h->Unseen . $h->Recent . $h->Deleted . $h->Answered . $h->Draft . $h->Flagged."]");
myLog($pfx."From: [". htmlspecialchars($h->fromaddress) . "] To: [".htmlspecialchars($h->toaddress)."]");
myLog($pfx."Subject: [$h->subject]");
/**
* Here you do whaterver you need with your email
*/
/**
* Backup email
*/
if (!($ret = imap_append($toMbox,$toMboxServerPath.$toMboxMailboxPath,$message)))
{
myLog("Error: ".imap_last_error());
die;
}
else
{
myLog("everything ok, mail [$fetchStartUID:$fetchEndUID] downloaded and moved in $newMailboxNameMOVE");
}
}
/**
* End
*/
imap_close($fromMbox);
imap_close($toMbox);
myLog("Connection closed");
?>
First, IMAP does not have a MOVE command only copy but even if it did you can copy from one IMAP server to another directly.
Why not use a subfolder in the account for backups. Download them to your local machine then COPY them to the subfolder and then DELETE them from the INBOX.
COPY and DELETE are imap server side commands so they don't have to leave the server to do the "move"
If both accounts are on the same server there is another option, allow access to the backup account's INBOX to the primary account user. Then you can use server side copy/delete to move it to the backup folder.
Not all IMAP servers allow for shared folders.
php does have a imap_move function but I assume it does a copy/delete.
I don't know any other solution like PHP.
But for your code and testing you should use:
$fromMboxServerPath = "{imap.from.server/notls/imap/readonly:143}"; //ReadOnly
in imap_append() you should give the date from emailheader. see PHP Manual: http://php.net/manual/en/function.imap-append.php
after that you will have a 1to1 copy of your mail in the target IMAP-Server.
Why separate account and all the hassle that will be involved? Can't you either
a) backup the mail account using standard backup tools like, eg. rdiff-backup?
b) back them up in the db?
or even
c) create an alias so that emails go to both accounts and you have different criteria for removing mails from both accounts (ie. keep them for one more month in the backup account)