I am using Indy10. My server is processing a form post. ARequestInfo.FormParams properly contains the unparsed form parameters. But ARequestInfo.Params.count is 0. Is there a way I can have ARequestInfo.Params have the parsed form parameters? Or is there a way to parse ARequestInfo.FormParams?
It seems I have to write my own parsing routine when this should already be encapsulated in the object. Or perhaps I am missing a method.
Update Doing some more digging I have found when doing a post within our LAN everything works ok. But when the post is done from a browser outside of our LAN it does not.
Try to set the ParseParams property at your TIdHTTPServer.Or you can make a descendant of the TIdHTTPRequestInfo class for accessing the protected method named DecodeAndSetParams to parse the parameters by yourself.Here is the example.
uses
IdCustomHTTPServer;
type
THTTPRequest = class(TIdHTTPRequestInfo);
procedure TForm1.Button1Click(Sender: TObject);
var
Request: THTTPRequest;
begin
Request := THTTPRequest.Create;
Request.DecodeAndSetParams('firstparam=1&secondparam=2&thirdparam=3');
ShowMessage('Param count: ' + IntToStr(Request.Params.Count) +
sLineBreak + sLineBreak +
Request.Params[0] + sLineBreak +
Request.Params[1] + sLineBreak +
Request.Params[2] + sLineBreak
);
Request.Free;
end;
Related
All their examples use HS*** with none in RS*** and trying to change the examples to suite dont seem to be working.
My problem seems to be getting the private key loaded for signing. I'm using a PEM in a string, setting up the claims, using this
Procedure RunTest2b;
var
LToken: TJWT;
LSigner: TJWS;
LKey: TJWK;
LAlg: TJOSEAlgorithmId;
s: String;
begin
LToken := TJWT.Create;
try
LToken.Claims.Subject := 'Paolo Rossi';
LToken.Claims.Issuer := 'Delphi JOSE Library';
LToken.Claims.IssuedAt := Now;
LToken.Claims.Expiration := Now + 1;
// Signing algorithm
LAlg := TJOSEAlgorithmId.RS256;
LSigner := TJWS.Create(LToken);
LKey := TJWK.Create(gPrivateKey);
try
// With this option you can have keys < algorithm length
LSigner.SkipKeyValidation := True;
LSigner.Sign(LKey, LAlg);
s := 'Header: ' + LSigner.Header + #13#10 +
'Payload: ' + LSigner.Payload + #13#10 +
'Signature: ' + LSigner.Signature + #13#10 +
'Compact Token: ' + LSigner.CompactToken;
if s = '' then;
finally
LKey.Free;
LSigner.Free;
end;
finally
LToken.Free;
end;
end;
This fails in the sign method saying "Unable to load private key:" and a bunch of weird characters which makes it look like maybe I have a wide string when I should have an ansistring, but changing it doesn't seem to help.
I have also tried using the TBase64.Decode and TBase64.UrlDecode to transform the key before I pass it into the sign method without success.
Can anyone see where I'm making a mistake ?
I recently jump thru a few hoops to do some JWT testing using JOSE. I didn't sign anything, but did have to use the PEM to verify the JWT which was using RS. While doing so I made the mistake of concatenating the PEM string into a single line of characters without preserving the line feeds. I wonder if you made the same mistake with your keys?
i.e. bad PEM format
myPem := '-----BEGIN PUBLIC KEY-----'
+ 'A23BBjhasdfbewisudvnacwerf823rdsvcp2'
+ 'bDenDfsub893rghvsaefawerd'
+ '-----END PUBLIC KEY-----';
i.e. good PEM format
myPem := '-----BEGIN PUBLIC KEY-----'
+ #13#10 + 'A23BBjhasdfbewisudvnacwerf823rdsvcp2'
+ #13#10 + 'bDenDfsub893rghvsaefawerd'
+ #13#10 + '-----END PUBLIC KEY-----';
I use TIdHTTP for web-service request, after autentification I must keep session information but IdCookieManager seems lost always one cookie...
This is my very simple snippet
procedure TForm1.Button5Click(Sender: TObject);
var
i : Integer;
Cookie : TIdCookies;
begin
Memo1.Lines.Clear;
try
IdHTTP1.AllowCookies := true;
IdHTTP1.CookieManager := IdCookieManager1;
IdHTTP1.Get(Edit2.Text);
if IdCookieManager1.CookieCollection.Count = 0
then Memo1.Lines.Add('Empty');
Cookie := IdCookieManager1.CookieCollection;
for i := 0 to Cookie.Count -1 do
Memo1.Lines.Add(Cookie.Cookies[i].Domain + ': ' + Cookie.Cookies[i].CookieName +
'=' + Cookie.Cookies[i].Value);
except
on E : Exception do
Memo1.Lines.Add(E.Message);
end;
end;
For example if I do IdHTTP1.Get('www.google.com'); I get two cookie (1P_JAR,NID), but if I do on a web browser it give three (1P_JAR,NID and CONSENT).
And this is for all URL, It seems like it "loses" always one cookie
You're trying to compare a single GET request response to a browser response. It's not the same thing.
If you use e.g. Postman you will get the very same result (2 cookies for www.google.com).
I think you should modify the service server side if you are in control of it or consume it in a different way if you are not.
I have an external message coming in every second.
The message payload is saved in a ClientDataSet and displayed in a dbGrid.
No data base is involved. RAM storage only.
This works fine,
BUT,
I have intermittent problems when the dataset is empty and populated the first time.
The code is as follows:
procedure TCtlCfg_DM_WarningsFaults_frm.DecodeRxFrame(Protocol: TProtocolSelection;
// PROVA UTAN VAR VAR Frame : CAN_Driver.TCAN_Frame);
Frame : CAN_Driver.TCAN_Frame);
var
OldRecNo : integer;
// OldIxname : string;
// bMark : TBookMark;
WasFiltered : Boolean;
IdBitFields : TCanId_IdBitFields;
Msg : TCan_Msg;
MsgType : integer;
GlobalNode : TCanId_GlobalNode;
LocalNode : TCanId_LocalNode;
SubNode : TCanId_SubNode;
EntryType : integer;
SubSystemType : integer;
SubSystemDeviceId : integer;
IsActive : Boolean;
IsAcked : Boolean;
begin
with cdsWarningsFaults do
begin
if not Active then Exit;
Msg := Frame.Msg;
IdBitFields := DecodeCanId(Protocol, Frame.ID);
if IdBitFields.SubNode <> cSubNode_Self then Exit; // Ignore non controller/slave messages
if IdBitFields.AddressMode <> cCanId_AddrMode_CA then Exit;
MsgType := IdBitFields.MessageType;
if MsgType <> cMsg_CTL_CA_Broadcast_WarningAndFaultList then Exit;
if Frame.MsgLength < 5 then Exit;
GlobalNode := IdBitFields.GlobalNode;
LocalNode := IdBitFields.LocalNode;
SubNode := IdBitFields.SubNode;
// Silent exit if wrong node
if GlobalNode <> fNodeFilter.GlobalNode then Exit;
if LocalNode <> fNodeFilter.LocalNode then Exit;
if SubNode <> fNodeFilter.SubNode then Exit;
EntryType := Msg[1];
SubSystemType := Msg[2];
IsActive := (Msg[3] = 1);
SubSystemDeviceId := Msg[4];
IsAcked := (Msg[8] = 1);
DisableControls; // 2007-12-03/AJ Flytta inte scrollbars under uppdatering
OldRecNo := RecNo;
// OldIxName := IndexName; // Save current index
// IndexName := IndexDefs.Items[0].Name;
WasFiltered := Filtered; // Save filter status
Filtered := False;
try
try
if Findkey([GlobalNode, LocalNode, SubNode, EntryType, SubSystemType, SubSystemDeviceId]) then
begin // Update record
Edit;
FieldByName('fIsActive').AsBoolean := IsActive;
FieldByName('fIsAcked').AsBoolean := IsAcked;
FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
Post;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Edit. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
end
else
begin // Create new record
Append;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Append. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
try
FieldByName('fGlobalNode').AsInteger := GlobalNode;
FieldByName('fLocalNode').AsInteger := LocalNode;
FieldByName('fSubNode').AsInteger := SubNode;
FieldByName('fEntryType').AsInteger := EntryType;
FieldByName('fSubSystemType').AsInteger := SubSystemType;
FieldByName('fSubSystemDeviceId').AsInteger := SubSystemDeviceId;
FieldByName('fIsActive').AsBoolean := IsActive;
FieldByName('fIsAcked').AsBoolean := IsAcked;
FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
finally
try
Post; // VArför biter inte denna post så att det blir edit nästa gång
except
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Exception efter Post.', True);
end;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Post. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
end;
end;
except
on E: Exception do
begin
MainForm.AddToActivityLog('Post exception message: [' + E.Message + ']', False);
MainForm.AddToActivityLog('Post exception class: [' + E.ClassName + ']', False);
MainForm.AddToActivityLog('Post exception Error code: [' + IntToStr(EDBCLIENT (E).ErrorCode) + ']', False);
MainForm.AddToActivityLog('Post exception ReadOnly is: [' + BoolToStr(ReadOnly) + ']', False);
MainForm.AddToActivityLog('Post exception CanModify is: [' + BoolToStr(CanModify) + ']', False);
MainForm.AddToActivityLog('DecodeRxFrame: Exception inside FindKey block', False);
Cancel;
end;
end;
finally
// IndexName := OldIxName; // Restore previous index
Filtered := WasFiltered; // Restore filter state
if (OldRecNo >= 1) and (OldRecNo <= RecordCount) then RecNo := OldRecNo;
EnableControls;
end;
end;
//MainForm.AddToActivityLog('DecodeRxFrame: Exit ur proceduren', False);
end;
The problem is when the record does not already exist,
and I need to Append a new record.
It often works fine, but many times it seems the POST does not work,
and the append is repeated a few or many times when new data comes in.
Suddenly the append works, and subbsequent updates are done using edit,
and as far as I can tell, after that it then works forever.
The issue is intermittent and the number of tries needed to succeed vary.
It feels like a timing issue, but I cannot figure it out.
Any ideas greatly appreciated.
Thanks,
Anders J
As mentioned in my comment a lot can be figured out about how the code flows using an extract of the logs. (Also as a side-note, sometimes you need to be careful of the reliability of your logging system. Your logging is at least partially home-brew, so I have no idea what it means when you arbitrarily pass True/False values to the AddToActivityLog method.)
However, I am still able to offer some guidance to help you identify your problem. I also have some general comments to help you improve your code.
You're not shy to use logging to narrow down your problem: this is a good thing!
However you technique could use a little improvement. You're trying to determine what's going wrong around the Post method. Given such a clear goal, your logging seems surprisingly haphazard.
You should do the following:
//Log the line before Post is called. It confirms when it is called.
//Log important state information to check you're ready to post "correctly"
//In this it's especially useful to know the Sate of the dataset (dsEdit/dsInsert).
Post;
//Log the line after Post to confirm it completed.
//Note that "completed" and "succeeded" aren't always the same, so...
//Again, you want to log important state information (in this case dsBrowse).
If you had this this logging, you might (for example) be able to tell us that:
Before calling Post dataset is in dsInsert state.
And (assuming no exceptions as you say): after calling Post the dataset is still in dsInsert state.
NOTE: If it were in dsBrowse but Post still considered "unsuccessful", you'd be told to log details of the record before and after Post.
So now: Post "completing" without the record being "posted" would give a few more things to look at:
What events are hooked to the data set? Especially consider events used for validation.
Since you're working with TClientDataSet there's an important event you'll want to take a look at OnPostError. DBClient uses this callback mechanism to notify the client of errors while posting.
If you log OnPostError I'm sure you'll get a better idea of the problem.
Finally I mentioned you have a lot of other problems with your code; especially the error handling.
Don't use with until you know how to use it correctly. When you know how to use it correctly, you'll also know there's never a good reason to use it. As it stands, your code is effectively 2 characters short of a subtle bug that could have been a nightmare to even realise it even existed; but would be a complete non-issue without with. (You declared and used a property called IsActive differing by only 2 characters from TDataSet's Active. I'm sure you didn't realise this; and their difference is but an accident. However, if they had been the same, with would very quietly use the wrong one.)
You need to write smaller methods - MUCH smaller! Blobs of code like you have are a nightmare to debug and are excellent at hiding bugs.
Your exception handling is fundamentally wrong:
Your comment about logging and exception handling suggests that you've been simply adding what you can out of desperation. I think it pays to understand what's going on to keep your logging useful and avoid the clutter. Let's take a close look at the most problematic portion.
/_ try
/_ FieldByName('fGlobalNode').AsInteger := GlobalNode;
/_E FieldByName('fLocalNode').AsInteger := LocalNode;
| FieldByName('fSubNode').AsInteger := SubNode;
| FieldByName('fEntryType').AsInteger := EntryType;
| FieldByName('fSubSystemType').AsInteger := SubSystemType;
| FieldByName('fSubSystemDeviceId').AsInteger := SubSystemDeviceId;
| FieldByName('fIsActive').AsBoolean := IsActive;
| FieldByName('fIsAcked').AsBoolean := IsAcked;
| FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
|_ finally
/_ try
/_ Post;
/ except
| MainForm.AddToActivityLog(..., True);
| end;
|_ MainForm.AddToActivityLog(..., False);
/ end;
|
...
So, in the above code:
If no exceptions happen, you'd simply step from one line to the next.
But as soon as an exception happens, you jump to the next finally/except block.
The first problem is: Why would you try to force a Post if you haven't finished setting your field values. It's a recipe for headaches when you end up with records that have only a fraction of the data they should - unless you're lucky and Post fails because critical data is missing.
When finally finishes during an exception, code immediately jumps to the next finally/except in the call-stack.
Except is slightly different, it only gets called if something did go wrong. (Whereas finally guarantees it will be called with/without an exception.
TIPS: (good for 99% of exception handling cases.
Only use try finally for cleanup that must happen in both success and error cases.
Nest your try finally blocks: The pattern is <Get Resource>try .... finally<Cleanup>end (The only place to do another resource protection is inside the .....)
Avoid except in most cases.
The main exception to the previous rule is when cleanup is needed only in the case of an error. In which case: do the cleanup and re-raise the exception.
Other than that: only implement an except block without re-raising if you can fully resolve an error condition. (Meaning the lines of code immediately after the exception swallower truly don't care about the previous exception.
I can successfully send an email with ShellExecute. The To address is correct, the Sender Address is correct, and the Subject is correct. The body of the email is correct, except there are no line breaks at all and everything appears as a single paragraph with no line breaks. The default email client in my case is Windows 8.1 mail.
My question is, can ShellExecute be used so the line breaks are retained? I am not looking to send the email directly with Indy. All I need to do is to send an email to the default email client and have it formatted correctly.
procedure TForm1.Email1Click(Sender: TObject);
var
iGridTableItem: TcxCustomGridTableItem;
iName, iDate, iEmail, iSubject, iBody, iParam: string;
begin
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('EMail');
if iGridTableItem.EditValue <> null then
iEmail := iGridTableItem.EditValue;
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('Name');
if iGridTableItem.EditValue <> null then
iName := iGridTableItem.EditValue;
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('Date');
if iGridTableItem.EditValue <> null then
iDate := DateToStr(iGridTableItem.EditValue);
iSubject := 'ImageEn EBook';
iBody := 'Dear Mr. ' + iName + ',' + sLineBreak + sLineBreak +
'PayPal has advised me that you purchased xxxxx on ' + iDate +
'.' + ' Thank-you for your purchase.' + sLineBreak + sLineBreak + 'You may ' +
'download the xxx at' + sLineBreak +
'http://www.xxxxx.xxx/xxx/EBook/xxx101.zip' + sLineBreak +
'Best regards,' + sLineBreak + 'William Miller' + sLineBreak +
'Adirondack Software and Graphics ' + sLineBreak + 'Email: xxx#xxxxxxxx.xxx'
iParam := 'mailto:' + iEmail + '?subject=' + iSubject + '&Body=' + iBody;
ShellExecute(Form1.Handle, 'open', PChar(iParam), nil, nil, SW_SHOWNORMAL);
end;
Different mail apps support the mailto protocol differently. Not all apps allow multiple parameters to be specified together, etc. So using mailto to send emails is going to be very spotty on different machines.
That being said, you are essentially invoking a URL, just one with the mailto: protocol scheme. As such, you need to url-encode reserved characters, like spaces and line breaks. You might not want to use Indy to send the mail, but you can use it to encode your parameter values, at least:
uses
..., IdURI;
iParam := 'mailto:' + iEmail + '?subject=' + TIdURI.ParamsEncode(iSubject) + '&Body=' + TIdURI.ParamsEncode(iBody);
I need to build a provider server in Delphi to send push notification messages to my iPhone app via APNS.
I have read that this is possible to do through Indy components. It is also required to install an SSL certificate (.p12) provided by apple.
I'm looking for some pointers to get started with this in Delphi.
What would be a good library to use, and does anyone know of any example code to do something like this?
Here are samples for Ruby & PHP, C# and JAVA
OK I managed this as follows:
Add an indy TidTCPClient and TIdSSLIOHandlerSocket on your form and link them. Set the SSL options in the TIdSSLIOHandlerSocket, set CertFile and KeyFile to the appropriate .pem files. Set method to sslvSSLv23 and mode to sslmClient.
In the IOHandler's OnGetPassword event set your key's password.
Useful URLs:
http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html
http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12
On the coding front:
N.b. HexData is the ID sent from the IPhone App
function SendNotification(HexData: String; Count: Integer): Boolean;
var
p_DataSize,
I: Integer;
p_payllen: Byte;
p_json : String;
p_sndmsg : String;
begin
// Delphi 6 so needed to create JSON by hand<br>
p_json := '{"aps":{';
if (Count > 0) then
begin
p_json := p_json + '"alert":"You Have ' + IntToStr(Count);
if (count = 1) then
p_json := p_json + ' Reminder'
else<br>
p_json := p_json + ' Reminders';
p_json := p_json + '","sound": "default",';
end;
p_json := p_json + '"badge":' + inttostr(Count) + '}}';
p_payllen := length(p_json);
// Hard code the first part of message as it never changes
p_sndmsg := chr(0) + chr(0) + chr(32);
// Convert hex string to binary data
p_DataSize := Length(HexData) div 2;
for I := 0 to p_DataSize-1 do
p_sndmsg := p_sndmsg + char(Byte(StrToInt('$' + Copy(HexData, (I*2)+1,
2))));
//Now need to add length of json string and string itself
p_sndmsg := p_sndmsg + chr(0) + Char(p_payllen) + p_json;
try
// According to Apple can't connect/disconnect for each message so leave open for later
if (not PushClient.Connected) then
PushClient.Connect;
PushClient.IOHandler.Send(p_sndmsg[1], length(p_sndmsg));
except
on e : exception do
Log_Error(e.message);
end;
end;
you can try porting java/php or ruby code as Rogier said. mean while have a look at his http://www.pushwoosh.com/ some thing similar to http://urbanairship.com/ .