Delphi TIdhttp Post JSON? - php

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);
?>

Related

sha1 in Go and PHP has different result

PHP Code:
$str = chr(164);
$resualt = sha1($str);
echo $resualt;
PHP resualt:
f5efcd994fca895f644b0ccc362aba5d6f4ae0c6
Golang code:
str := string(164)
//fmt.Println(str)
passSha1 := sha1.New()
passSha1.Write([]byte(str))
getSha1 := passSha1.Sum(nil)
fmt.Printf("%x\n",getSha1)
Golang resualt:
fe33a6b4de93e363cf1620f7228df4164d913fbf
In Go, how can I get the same result like PHP.
Your php code is encoding a 1-byte input, but your Go code is doing the same on a utf-8 encoded string. If you print len(string(164)) you'll see that it is 2-bytes. Use this:
str := []byte{164}
passSha1 := sha1.New()
passSha1.Write([]byte(str))
getSha1 := passSha1.Sum(nil)
fmt.Printf("%x\n",getSha1)

how to get values from a interface in golang

I need to get values from a serialized string which generated from php code
So I use a package named:php_serialize to unserialize the string and then got a result of interface{} type .
But I have no idea how to get values inside the result.
This is code:
package main
import (
"github.com/yvasiyarov/php_session_decoder/php_serialize"
"fmt"
)
func main() {
// this string is generated from php code
str := `a:3:{s:4:"name";s:3:"tom";s:3:"age";s:2:"23";s:7:"friends";a:2:{i:0;a:1:{s:4:"name";s:5:"jerry";}i:1;a:1:{s:4:"name";s:4:"jack";}}}`
decoder := php_serialize.NewUnSerializer(str)
if result, err := decoder.Decode(); err != nil {
panic(err)
} else {
fmt.Println(result)
}
}
The print result is :
map[name:tom age:23 friends:map[0:map[name:jerry] 1:map[name:jack]]]
This result is a php_serialize.PhpValue type, which is interface{} type
The next step is how to get values inside the result?
such as get the age field and value
You must assert the result to map[string]interface:
mResult := result.(map[string]interface{})
fmt.Println(mResult["name"])
And once more assertion for friends:
mFriends := mResult["friends"].(map[int]map[string]interface{})
Then use it: mFriends[0]["name"]
Here some ways to access the data:
package main
import (
"fmt"
"github.com/yvasiyarov/php_session_decoder/php_serialize"
)
func main() {
// this string is generated from php code
str := `a:3:{s:4:"name";s:3:"tom";s:3:"age";s:2:"23";s:7:"friends";a:2:{i:0;a:1:{s:4:"name";s:5:"jerry";}i:1;a:1:{s:4:"name";s:4:"jack";}}}`
decoder := php_serialize.NewUnSerializer(str)
result, err := decoder.Decode()
if err != nil {
panic(err)
}
fmt.Println(result)
// simple assert
t := result.(php_serialize.PhpArray)
// use php_seriale build in function to get string
strVal := php_serialize.PhpValueString(t["name"])
fmt.Println(strVal)
// type switch in case of different valid types
switch t := result.(type) {
default:
fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has
case php_serialize.PhpArray:
fmt.Println(t)
fmt.Println(t["name"])
fmt.Println(t["age"])
// should be done recursively...
switch f := t["friends"].(type) {
default:
fmt.Printf("unexpected type %T\n", f) // %T prints whatever type t has
case php_serialize.PhpArray:
fmt.Println(f)
fmt.Println(f[0])
fmt.Println(f[1])
}
}
}
I hope this gives you some ideas.
Basic concept
php_serialize has built in functions to convert primitives.
Variable structures are represented with built in types which need to be used to access the structure.

how to duplicate openssl_encrypt?

I was hoping someone had already implemented this in golang as I am far from even good at cryptography. However in porting a project from php to golang I have run into an issue with porting the openssl_encrypt method found here. I have also dug into the source code a little with no avail.
Here is the method I have implemented in golang. which gives me the output
lvb7JwaI4OCYUrdJMm8Q9uDd9rIILnvbZKJb/ozFbwCmLKkxoJN5Zf/ODOJ/RGq5
Here is the output I need when using php.
lvb7JwaI4OCYUrdJMm8Q9uDd9rIILnvbZKJb/ozFbwDV98XaJjvzEjBQp7jc+2DH
And here is the function I used to generate it with php.
$data = "This is some text I want to encrypt";
$method = "aes-256-cbc";
$password = "This is a really long key and su";
$options = 0;
$iv = "MMMMMMMMMMMMMMMM";
echo openssl_encrypt($data, $method, $password, $options, $iv);
To me it looks like it is very close and I must be missing something obvious.
You were very close, but you had the padding wrong. According to this answer (and the PHP docs), PHP uses the default OpenSSL padding behavior, which is to use the required number of padding bytes as the padding byte value.
The only change I made was:
copy(plaintextblock[length:], bytes.Repeat([]byte{uint8(extendBlock)}, extendBlock))
You can see the full updated code here.
Others beat me to the answer while I was playing with it, but I have a "better" fixed version of your example code that also takes into account that padding is always required (at least to emulate what the php code does).
It also shows the openssl command line that you'd use to do the same thing, and if available runs it (of course the playground won't).
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"log"
"os/exec"
"strings"
)
func main() {
const input = "This is some text I want to encrypt"
fmt.Println(opensslCommand(input))
fmt.Println(aesCBCenctypt(input))
}
func aesCBCenctypt(input string) string {
// Of course real IVs should be from crypto/rand
iv := []byte("MMMMMMMMMMMMMMMM")
// And real keys should be from something like PBKDF2, RFC 2898.
// E.g. use golang.org/x/crypto/pbkdf2 to turn a
// "passphrase" into a key.
key := []byte("This is a really long key and su")
// Make sure the block size is a multiple of aes.BlockSize
// Pad to aes.BlockSize using the pad length as the padding
// byte. If we would otherwise need no padding we instead
// pad an entire extra block.
pad := (aes.BlockSize - len(input)%aes.BlockSize)
if pad == 0 {
pad = aes.BlockSize
}
data := make([]byte, len(input)+pad)
copy(data, input)
for i := len(input); i < len(input)+pad; i++ {
data[i] = byte(pad)
}
cb, err := aes.NewCipher(key)
if err != nil {
log.Fatalln("error NewCipher():", err)
}
mode := cipher.NewCBCEncrypter(cb, iv)
mode.CryptBlocks(data, data)
return base64.StdEncoding.EncodeToString(data)
}
// Just for comparison, don't do this for real!
func opensslCommand(input string) string {
iv := []byte("MMMMMMMMMMMMMMMM")
key := []byte("This is a really long key and su")
args := []string{"enc", "-aes-256-cbc", "-base64"}
// "-nosalt", "-nopad"
args = append(args, "-iv", fmt.Sprintf("%X", iv))
args = append(args, "-K", fmt.Sprintf("%X", key))
cmd := exec.Command("openssl", args...)
// Show how you could do this via the command line:
fmt.Println("Command:", strings.Join(cmd.Args, " "))
cmd.Stdin = strings.NewReader(input)
result, err := cmd.CombinedOutput()
if err != nil {
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
// openssl not available
return err.Error() // XXX
}
// some other error, show it and the (error?) output and die
fmt.Println("cmd error:", err)
log.Fatalf("result %q", result)
}
// Strip trailing '\n' and return it.
if n := len(result) - 1; result[n] == '\n' {
result = result[:n]
}
return string(result)
}
Playground

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.

Indy is sending empty message when i use POST method

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;

Categories