Indy is sending empty message when i use POST method - php

I have to upload a file with my delphi program and handle server side with php
This is my complete code :
unit Unit6;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
Vcl.ComCtrls, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,IdSSLOpenSSL;
type
TForm6 = class(TForm)
IdHTTP1: TIdHTTP;
BitBtn1: TBitBtn;
od1: TOpenDialog;
ProgressBar1: TProgressBar;
m1: TMemo;
IdIOHandlerStack1: TIdIOHandlerStack;
procedure BitBtn1Click(Sender: TObject);
procedure HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode;const AWorkCountMax: Integer);
procedure IdHTTPWork(Sender: TObject; AWorkMode: TWorkMode;const AWorkCount: Integer);
procedure IdHTTPWorkEnd(Sender: TObject; AWorkMode: TWorkMode);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form6: TForm6;
implementation
{$R *.dfm}
procedure TForm6.HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCountMax: Integer);
begin
if AWorkMode = wmRead then
begin
ProgressBar1.Max := AWorkCountMax;
ProgressBar1.Position := 0;
end;
end;
procedure TForm6.IdHTTPWork(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
if AWorkMode=wmRead then
ProgressBar1.Position := AWorkCount;
end;
procedure TForm6.IdHTTPWorkEnd(Sender: TObject; AWorkMode: TWorkMode);
begin
ProgressBar1.Position := 0;
end;
procedure TForm6.BitBtn1Click(Sender: TObject);
var
Response:string;
LHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
if od1.Execute then
begin
// LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
// IdHTTP1.IOHandler:=LHandler;
Response := IdHTTP1.Post('http://localhost/delphi/index.php?asd', od1.FileName);
m1.Text := Response;
end;
end;
end.
The server side is in php:
test1
<?php
print_r($_FILES);
?>
also i changed it to:
test1
<?php
print_r($_POST);
?>
but at the all tests the delphi response is an empty array.
test1
Array
(
)
Which part of my code is the problem?!

You are passing a filename to TIdHTTP.Post(). That posts the raw content of the file as-is. That will not populate PHP's $_FILES array (used for multipart/form-data posts) or $_POST array (used for application/x-www-form-urlencoded posts).
Add the file to a TIdMultipartFormDataStream and post that instead. It will send a multipart/form-data post that should populate the $_FILES array, eg:
uses
..., IdMultipartFormData;
procedure TForm6.BitBtn1Click(Sender: TObject);
var
//LHandler: TIdSSLIOHandlerSocketOpenSSL;
PostData: TIdMultipartFormDataStream;
begin
if od1.Execute then
begin
//LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
//IdHTTP1.IOHandler := LHandler;
PostData := TIdMultipartFormDataStream.Create;
try
PostData.AddFile('file', od1.FileName);
m1.Text := IdHTTP1.Post('http://localhost/delphi/index.php?asd', PostData);
finally
PostData.Free;
end;
end;
end;

Related

Base64 Encoding Delphi

I'm making a Delphi application and I'm trying to encode with base64 encoding stream and I made two functions:
function EncodedInputParams(input: TStringStream): string;
var
output: TStringStream;
encoder: TIdEncoderMIME;
begin
input.Encoding.UTF8;
input.Position := 0;
output := TStringStream.Create;
try
encoder := TIdEncoderMIME.Create(nil);
encoder.Encode(input, output);
output.Seek(0, soFromBeginning);
Result := output.DataString;
finally
output.Free;
end;
end;
function SecondEncoding(input: TStringStream): string;
var
output: TStringStream;
midRes: string;
begin
input.Encoding.UTF8;
input.Position := 0;
output := TStringStream.Create;
try
EncodeStream(input, output);
output.Seek(0, soFromBeginning);
midRes := output.DataString;
midRes := StringReplace(midRes, #13#10, EmptyStr, [rfReplaceAll]);
result := midRes;
finally
output.Free;
end;
end;
Functions are working fine but the problem is that these solutions return different string than a string encoded same way in PHP: http://www.tools4noobs.com/online_php_functions/base64_encode/ .
Any ideas how to rewrite one of the function to return string like one written in PHP.

how to translate zend_parse_parameters from c++ to pascal?

I need parse PHP extension parameters using lazarus/freepascal, but I do not know how to define the C function zend_parse_parameters in lazarus as:
int zend_parse_parameters ( int num_args TSRMLS_DC, char* type_spec, ... )
Can anybody help me?
Probably something like
uses ctypes;
function zend_parse_parameters(num_args:cint;type_spec:pchar):cint;cdecl; varargs;
but I don't know what to do with the macro (I assume it is merely a helper macro that links together the variable part and the num_args).
here is my test code of lazarus under CENTOS6.3:
procedure encryptext (ht : integer; return_value : pzval; this_ptr : pzval;
return_value_used : integer; TSRMLS_DC : pointer); cdecl;
var
getmyparameters: function(argu_num:integer;
type_apec:pansichar;Args : Array of const):integer;cdecl;
a:integer;
rtnstr:string;
begin
if (PHPLib < 1) then exit;
getmyparameters := GetProcAddress(PHPLib, 'zend_parse_parameters');
if (#getmyparameters = nil) then
begin
raise EPHP4DelphiException.Create('zend_parse_parameters');
exit;
end;
if ht < 1 then
begin
zend_wrong_param_count(TSRMLS_DC);
Exit;
end;
a := 1;
if (getmyparameters(ht,pansichar('s'),[pansichar(rtnstr),#a]) <> SUCCESS ) then exit;
ZVAL_STRING(return_value,pansichar(rtnstr),true);
end;
php code of /var/www/html/a.php:
<?php
echo encryptext('hello');
?>
the error message is:
PHP Warning: encryptext() expects exactly 0 parameters, 1 given in /var/www/html/a.php on line 2

Delphi TIdhttp Post JSON?

Anybody getting JSON to work with TIdHttp ?
The PHP always return NULL in the $_POST, am I doing anything wrong ?
Delphi source:
http := TIdHttp.Create(nil);
http.HandleRedirects := True;
http.ReadTimeout := 5000;
http.Request.ContentType := 'application/json';
jsonToSend := TStringStream.Create('{"name":"Peter Pan"}');
jsonToSend.Position := 0;
Memo1.Lines.Text := http.Post('http://www.website.com/test.php', jsonToSend);
jsonToSend.free;
http.free;
PHP source:
<?php
$value = json_decode($_POST);
var_dump($value);
?>
You can't use a TStringList to post JSON data. TIdHTTP.Post() will encode the TStringList contents in a way that breaks the JSON data. You need to put the JSON data into a TStream instead. TIdHTTP.Post() will transmit its contents as-is. Also, don't forget to set the TIdHTTP.Request.ContentType property so the server knows you are posting JSON data.
You need to define a post variable, try this code (I have added "json" var to your code):
Delphi code:
http := TIdHttp.Create(nil);
http.HandleRedirects := true;
http.ReadTimeout := 5000;
jsonToSend := TStringList.create;
jsonToSend.Text := 'json={"name":"Peter Pan"}';
Memo1.Lines.Text := http.Post('http://www.website.com/test.php', jsonToSend);
jsonToSend.free;
http.free;
PHP source:
<?php
$value = json_decode($_POST['json']);
var_dump($value);
?>

Convert this php digital signing to Delphi

I would like to rewrite this php digital signing function into Delphi code.
function SaySig() {
$privKeyFilePath = "c:\temp\myrsakey.pem";
$data = "sign this string";
$fp = fopen($privKeyFilePath, "r");
$priv_key = fread($fp, 8192);
fclose($fp);
$privatekeyid = openssl_get_privatekey($priv_key);
openssl_sign($data, $signature, $privatekeyid, OPENSSL_ALGO_SHA1);
openssl_free_key($privatekeyid);
$sig = base64_encode($signature);
echo "<br>";
echo "Signature:".$sig."<br><br>";
}
I don't care which component is used (lockbox, DelphiOpenSSL, Chilkat Crypt ActiveX, etc). I have tried all of these with no success; which makes me think I am doing something fundamentally wrong.
My key was generated as such:
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj "/C=US/ST=CA/L=Mountain View/CN=www.mycompany.com" -keyout myrsakey.pem -out c:\temp\myrsacert.pem
The contents of the key (don't worry - I will generate a new one):
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQCqsR7s4X74LfTiLv1PP6Yn0SBpGBtbzkBSQ95E2b9Haa3Qtf0a
KjDJpZLMwXC/IrSP7K2Gxbl2cZotT19GVgw6PcYPTBBWX2gJoVrnQZP8uPdlGAgS
plODP55R9f4F0KzIpE6d+dpTGfJ1wysFqYN8fxtlu8K7YO/Mh8tNzN5VOQIDAQAB
AoGBAIvCvRyeQlU5Y+JzMSvbZNQDUrNabsRL67SwJ2VemVUCvbQ/3v62fv4M2VdY
KFYIN6oE08yfRw0pVWE2NT+lIxqSQx7+qv84Y7duqT7155wpCFj+a/6pYyNTFNFi
5wiTnN13eyHNgKxZm7QcMH67T/noTgz0LoT5p54ynmfNcjyBAkEA3DCEQ6Dm2xYH
Nhk3+7sNEVklN20zNqyYvrCunNLAiLioF1jDApdfcT8YtVd29L7tH1ZdJYG5DXJ8
Bs7eKLGekQJBAMZzy0Q7LZHdWQxSRi7wy0eq6SqZMqi0pb9VPuXjWG1y+rtRr1vV
vyMaGz4rcE7mkbq/Nkn+AQXc30GOj3GE8CkCQQCMDVwDfBN6pL8/fLjsJ+S+9RnD
8HRTwWKCX/UgkLif/fwEpZOcUVYGvSBlL9XdBJfkh9VFZwaidABJgEk0Tw3RAkBd
6pjMnpDvUeh9e0Y5mr0pGookHcIqsuspxEby9od3rI1aLsslU9+T1hwEbPxGarmW
vj0MAUgspR2G4deiqn4ZAkEAnWxV7NhtVPLs5Y2ZYeHz7ipdcSL4/keLW4PwKerF
7LJj4s7/6ZqnHA6Z0yhCcziflYQArWt1ViLMIYZ8grr5Kg==
-----END RSA PRIVATE KEY-----
And the output looks as such:
jcIIsr145dTwDrT8g4jb2HZ5FP5UL6/9mK7hF6hC2lCZGlM0W4QqFqytghWaU0w3Z6JkMVUlxxWtQ2R+
vWQVB0F3htAtbVZkiA67x0zor+zmpClBIazmfVJlng4sG1R7CCUZ0gGhdm4JMc08VsWU25utudcG6inpl
whQiZgefW0=
It might appear as if I am requesting someone to "do my work". But I have been pounding away at this for some time with no success.
Thanks.
Someone requested I post some of my code. Below are some of the things I have tried..
function TMainWeb.sign(mstring: String): string;
var
mPrivateKey: TLbRSAKey;
LbRSASSA1: TLbRSASSA;
begin
LbRSASSA1:= TLbRSASSA.create(nil);
LbRSASSA1.PrivateKey.LoadFromFile('C:\temp\myrsakey.der');
LbRSASSA1.HashMethod := TRSAHashMethod(hmSHA1);
LbRSASSA1.SignString(mString);
result := LbRSASSA1.Signature.IntStr;
end;
function TMainWeb.sign1(mstring: String): string;
var
LbDSA1: TLbDSA;
mPrivateKey: TLbRSAKey;
begin
mPrivateKey := TLbRSAKey.Create(aks1024);
mPrivateKey.LoadFromFile('C:\temp\myrsakey.der');
LbDSA1 := TLbDSA.create(application);
lbDSA1.PrivateKey.Assign(mPrivateKey);
LbDSA1.SignString(mString);
end;
function TMainWeb.Sign2(mString: String): string;
var
signer: TMessageSigner;
begin
signer := TMessageSigner.Create;
signer.LoadPrivateKey('C:\temp\myrsakey.pem');
signer.PlainMessage := mString;
signer.MIMESign;
result := signer.SignedMessage;
end;
Try this.
I don't claim it's perfect code(!) but it compiles :-) and gives the same result you've quoted. Uses the OpenSSL API from M Ferrante that you mention above. A lot of the stuff it does you would normally only do once at startup - such as load private key, InitSSL etc. I use the Jedi JCL for base64 stuff - it's more straightforward.
Also some of this looks a bit odd (uses TBytes where PChar would do etc etc) as I originally wrote it using my modified Delphi 2010 API headers but then realised you were using D2007 and TEncoding wasn't available and a few mods were needed.
(SignStringToBase64 is the main call, right at the bottom of the listing)
uses libeay32, jclmime;
const
LIBEAY_DLL_NAME = 'libeay32.dll';
// These aren't defined in the original libeay32.pas file
procedure EVP_MD_CTX_init(ctx: PEVP_MD_CTX); cdecl; external LIBEAY_DLL_NAME;
function EVP_MD_CTX_cleanup(ctx: PEVP_MD_CTX): integer; cdecl; external LIBEAY_DLL_NAME;
procedure InitSSL;
begin
OpenSSL_add_all_algorithms;
OpenSSL_add_all_ciphers;
OpenSSL_add_all_digests;
ERR_load_crypto_strings;
// Seed the pseudo-random number generator
// This should be something a little more "random"!
RAND_load_file('c:\windows\paint.exe', 512);
end;
procedure FinalizeSSL;
begin
EVP_cleanup;
end;
function GetSSLErrorMessage: string;
const
BUFF_SIZE = 128; // OpenSSL docs state should be >= 120 bytes
var
err: TBytes;
begin
SetLength(err, BUFF_SIZE);
ERR_error_string(ERR_get_error, #err[0]);
result := string(err);
end;
function RSALoadPrivateKey(const AFileName, APassPhrase: string): PRSA;
var
bp: pBIO;
fn, pp: PAnsiChar;
pk: PRSA;
begin
fn := PAnsiChar(AnsiString(AFileName));
pp := PAnsiChar(AnsiString(APassPhrase));
bp := BIO_new(BIO_s_file());
BIO_read_filename(bp, fn);
pk := nil;
result := PEM_read_bio_RSAPrivateKey(bp, pk, nil, pp);
if result = nil then
raise Exception.Create('Private key failure.' + GetSSLErrorMessage);
end;
function LoadPrivateKey(const AFileName, APass: string): PEVP_PKEY;
var
rkey: PRSA;
begin
rkey := RSALoadPrivateKey(AFileName, APass);
result := EVP_PKEY_new;
EVP_PKEY_assign(result, EVP_PKEY_RSA, rkey);
end;
procedure CleanUpKey(AKey: PEVP_PKEY);
begin
if (AKey <> nil) then
begin
EVP_PKEY_free(AKey);
// The OpenSSL docs state that the related rsa key will also
// be freed when the parent key is freed
end;
end;
function EVPSign(ASource: TBytes; const APrivateKey: PEVP_PKEY): TBytes;
var
keysize: integer;
ks: cardinal;
ctx: EVP_MD_CTX;
begin
keysize := EVP_PKEY_size(APrivateKey);
SetLength(result, keysize);
EVP_MD_CTX_init(#ctx);
try
EVP_SignInit(#ctx, EVP_sha1);
EVP_SignUpdate(#ctx, #ASource[0], Length(ASource));
EVP_SignFinal(#ctx, #result[0], ks, APrivateKey);
SetLength(result, ks);
finally
EVP_MD_CTX_cleanup(#ctx);
end;
end;
function Base64EncodeBytes(Input: TBytes): string;
var
b64: TBytes;
begin
SetLength(b64, jclMime.MimeEncodedSizeNoCRLF(Length(Input)));
jclMime.MimeEncodeNoCRLF(Input[0], Length(Input), b64[0]);
result := string(b64);
end;
function SignStringToBase64(const AText: string): string;
var
key: PEVP_PKEY;
src, enc: TBytes;
begin
InitSSL;
try
key := LoadPrivateKey('c:\temp\priv-key.pem', '');
try
SetLength(src, Length(AText));
CopyMemory(#src[0], #AText, Length(AText));
enc := EVPSign(src, key);
result := Base64EncodeBytes(enc);
finally
CleanUpKey(key);
end;
finally
FinalizeSSL;
end;
end;

How can I capture output from the Windows cmd shell?

Is there any way with, say Perl or PHP, that I can grab output from another process that outputs to the Windows cmd shell? I have a game server that outputs certain information, for example say 'player finished track in 43s' and I want to grab that line and use Perl or PHP to send a request to a webserver to update ranks on a web page. Is there a way to grab that output pipe in Perl or PHP? Or could I achieve this using C++ Windows API maybe?
Let me clarify here: I want to execute a seperate Perl or PHP script that grabs output from the Windows cmd shell, and the output that is being displayed to the Windows cmd shell is coming from a different process.
You could use IPC::Open3 to read from the other process' standard output. Note that inter-process communication assumes a parent/child relationship between the processes. If that's not the case... I'm not aware of a mechanism for attaching to the output of a pre-existing process. In that case you may need to alter the producer to write data to a log file (or database) that your application can read from.
If all you care about is STDOUT, you can just use open2 from IPC::Open2:
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
#if there are arguments pretend to be the server
#for this example
if (#ARGV) {
local $| = 1;
for my $i (1 .. 100) {
print "pid $$ iter $i\n";
sleep 1;
}
exit;
}
#run perl with the current script as its argument,
#pass in an arg so that we trigger the behaviour
#above
open2 my $out, my $in, $^X, $0, 1
or die "could not run '$^X $0 1': $!\n";
while (<$out>) {
s/[\r\n]//g;
print "pid $$ saw [$_]\n";
}
You need to start your server within Perl:
my $server_out = `server.exe`; # Note the backticks.
Now $server_out contains the output of server.exe. But the trick here is that you have to wait until server.exe exits to get the out put.
Try IPC::Run (which is not a core module)
use English;
use IPC::Run;
my ($stdout, $stderr);
IPC::Run::run([$cmd, $arg1, $arg2, $argN], \undef, \$stdout, $stderr);
while(<$stdout>) {
print "Cmd said $_\n";
}
Note: Code not tested.
Found the info here.
Capturing the output in Perl is as simple as:
$output = qx(command);
or
$output = `command`; # backticks
Refer: perldoc perlop
This code redirects the STDOUT of a console application to a stringlist, which you can use on a memo for example. It's Delphi code, but in C++ the basic idea is exactly the same.
I use it to run console applications hidden, while redirecting the output to my own application, to show in a pane. It adds a new line to AStrings as soon as data comes in, so you'll have access to the output of the other application before it finishes.
procedure RunConsoleApp(const CommandLine: string; AStrings: TStrings);
type
TCharBuffer = array[0..MaxInt div SizeOf(Char) - 1] of Char;
const
MaxBufSize = 1024;
var
I: Longword;
SI: TStartupInfo;
PI: TProcessInformation;
SA: PSecurityAttributes;
SD: PSECURITY_DESCRIPTOR;
NewStdIn: THandle;
NewStdOut: THandle;
ReadStdOut: THandle;
WriteStdIn: THandle;
Buffer: ^TCharBuffer;
BufferSize: Cardinal;
Last: WideString;
Str: WideString;
ExitCode_: DWORD;
Bread: DWORD;
Avail: DWORD;
begin
GetMem(SA, SizeOf(TSecurityAttributes));
case Win32Platform of
VER_PLATFORM_WIN32_NT:
begin
GetMem(SD, SizeOf(SECURITY_DESCRIPTOR));
SysUtils.Win32Check(InitializeSecurityDescriptor(SD, SECURITY_DESCRIPTOR_REVISION));
SysUtils.Win32Check(SetSecurityDescriptorDacl(SD, True, nil, False));
SA.lpSecurityDescriptor := SD;
end; {end VER_PLATFORM_WIN32_NT}
else
SA.lpSecurityDescriptor := nil;
end; {end case}
SA.nLength := SizeOf(SECURITY_ATTRIBUTES);
SA.bInheritHandle := True;
SysUtils.Win32Check(CreatePipe(NewStdIn, WriteStdIn, SA, 0));
if not CreatePipe(ReadStdOut, NewStdOut, SA, 0) then
begin
CloseHandle(NewStdIn);
CloseHandle(WriteStdIn);
SysUtils.RaiseLastWin32Error;
end; {end if}
GetStartupInfo(SI);
SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
SI.wShowWindow := {SW_SHOWNORMAL} SW_HIDE;
SI.hStdOutput := NewStdOut;
SI.hStdError := NewStdOut;
SI.hStdInput := NewStdIn;
if not CreateProcess(nil, PChar(CommandLine), nil, nil, True, CREATE_NEW_CONSOLE, nil, nil, SI, PI) then
begin
CloseHandle(NewStdIn);
CloseHandle(NewStdOut);
CloseHandle(ReadStdOut);
CloseHandle(WriteStdIn);
SysUtils.RaiseLastWin32Error;
end; {end if}
Last := '';
BufferSize := MaxBufSize;
Buffer := AllocMem(BufferSize);
try
repeat
SysUtils.Win32Check(GetExitCodeProcess(PI.hProcess, ExitCode_));
PeekNamedPipe(ReadStdOut, Buffer, BufferSize, #Bread, #Avail, nil);
if (Bread <> 0) then
begin
if (BufferSize < Avail) then
begin
BufferSize := Avail;
ReallocMem(Buffer, BufferSize);
end; {end if}
FillChar(Buffer^, BufferSize, #0);
Windows.ReadFile(ReadStdOut, Buffer^, BufferSize, Bread, nil);
Str := Last;
I := 0;
while (I < Bread) do
begin
case Buffer^[I] of
#0: inc(I);
#7: begin
inc(I);
Windows.Beep(800, 50);
Str := Str + '^';
end;
#10:
begin
inc(I);
AStrings.Add(Str);
Str := '';
end; {end #10}
#13:
begin
inc(I);
if (I < Bread) and (Buffer^[I] = #10) then
inc(I);
AStrings.Add(Str);
Str := '';
end; {end #13}
else
begin
Str := Str + Buffer^[I];
inc(I);
end; {end else}
end; {end case}
end; {end while}
Last := Str;
end; {end if}
Sleep(1);
Application.ProcessMessages;
until (ExitCode_ <> STILL_ACTIVE);
if Last <> '' then
AStrings.Add(Last);
finally
FreeMem(Buffer);
end; {end try/finally}
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
CloseHandle(NewStdIn);
CloseHandle(NewStdOut);
CloseHandle(ReadStdOut);
CloseHandle(WriteStdIn);
end; {end procedure}
Here is a PHP specific solution, the project allows PHP to obtain and interact dynamically with a real cmd terminal. Get it here: https://github.com/merlinthemagic/MTS
After downloading you would simply use the following code:
//if you prefer Powershell, replace 'cmd' with 'powershell'
$shellObj = \MTS\Factories::getDevices()->getLocalHost()->getShell('cmd');
$strCmd1 = 'some_app.exe -param "test"';
$return1 = $shellObj->exeCmd($strCmd1);
The return will give you the command return OR error from cmd, just as if you sat at the console.
Furthermore, you can issue any command you like against the $shellObj, the environment is maintained throughout the life of the PHP script. So instead of bundling commands in a script file, just issue them one by one using the exeCmd() method, that way you can also handle the return and any exceptions.

Categories