IMAP GetLineResponse equivalent in Indy 10? - delphi

In Indy9, there was a method of the TIdImap class called GetLineResponse. This method has been removed in Indy10, so I'm wondering what I should use instead of this command in Indy10.
Here's an example of how I was using GetLineResponse in Indy9 as part of a method to download the first so many lines or bytes of an email:
IMAP.WriteLn('xx FETCH '+IntToStr(MsgNum)+' BODY.PEEK[TEXT]<0.'+
IntToStr(LineCount*70)+'>');
Result := IMAP.GetLineResponse('xx',[wsOK]) = wsOK;

There is no equivalent of TIdIMAP4.GetLineResponse() in Indy 10. Use TIdIMAP4.GetResponse() instead (which also exists in Indy 9). Or better, use TIdIMAP4.SendCmd() instead (which also exists in Indy 9) and let it handle the response for you.

Related

What is corresponding to GStack.HostToNetwork() in Indy10

My Environment:
C++ Builder XE4
Indy 10.6.0.4975
I am searching information on conversion from host byte order to network byte order.
I found info for Indy 9 using GStack.HostToNetwork(). However, I do not find one for Indy10.
What is corresponding one for Indy10?
You have your information backwards.
There were no TIdStack::HostToNetwork() (or TIdStack::NetworkToHost()) methods in Indy 9. They were TIdStack::WSHToN...() (and TIdStack::WSNToH...()) instead.
TIdStack::HostToNetwork() is for Indy 10. There are also HostToLittleEndian() (and LittleEndianToHost()) functions in IdGlobal.hpp.

TIdMessageParts.CountParts Returns 0

I'm trying to pull multipart emails in MIME format from an IMAP server using Indy 10.5.5 in Delphi 2010. These are the lines of code that I'm having trouble with are below, where I instatiate the curMessage object, retrieve a message into it, and then call CountParts:
var
curMessage: TIdMessage;
IMAP4: TIdIMAP4;
msgIndex: Integer;
begin
...
curMessage := TIdMessage.Create(nil);
IMAP4.Retrieve(msgIndex, curMessage);
curMessage.MessageParts.CountParts;
//code that checks counts
//and
end;
I then have some code that checks the various count properties of curMessage.MessageParts (i.e. TextPartCount). However, the CountPart procedure isn't returning anything, because the Count property referenced in the procedure block is 0, even though I've verified that the message is retrieved and placed into the curMessage.
One thing I've noticed, and haven't gotten to the bottom of yet, is that IsMsgSinglePartMime is coming back as true, even though all the messages on the server have Content-Type: multipart/mixed;.
Any help would be really appreciated.
What am I missing here? I can provide more code if needed,
Without seeing the actual email data, it is difficult to say for sure exactly why the data is not where you expect it to be. But if the TIdMessage.IsMsgSinglePartMime is getting set to True then that means that either:
TIdMessage.Encoding is meMIME but TIdMessage.MIMEBoundary.Count is 0, meaning there was no MIME boundary value detected in the top-level Content-Type header. If the Content-Type is a 'multipart/...' type, a boundary is required. If it is present, it is likely malformed in a way that prevented Indy from parsing it.
TIdMessage.Encoding is mePlainText but TIdMessage.ContentTransferEncoding is either 'base64' or 'quoted-printable'.
In either case, if there is body content present then it would end up in the TIdMessage.Body property if it is textual data, otherwise it would end up in the TIdMessage.MessageParts as an attachment instead. Since TIdMessage.MessageParts.Count is 0 in your case, the data is either in TIdMessage.Body, or is got discarded.
You may want to consider upgrading to a newer Indy version. The version shipped with D2010 is pretty old, and there have been fixes/changes made to TIdIMAP4 and TIdMessage (and its internal parsers) in recent years.

Seeking very simple TIdCommandHandler conditional response example

Delphi XE2, so Indy 10.
My client sends a command which is processed by a TIdCommandHandler of my TIdCmdTCPServer.
I want to be able to perform some logic and return either a success or fail response and check for that back at the client.
Can someone please point me at a few lines of code as an example? Thanks in advance.
Well, here's the simplest demo.
Add an IdCmdTCPServer to your form, and add one command, set its name in the Command property, I originally thought I should handle Response in OnCommand event like this:
procedure TForm1.IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
begin
//ASender.Response.Add('Hello'); // wrong way
ASender.Reply.SetReply(0,'HELLO');
end;
Update Remy pointed out I shouldn't be using Response.
So you want to return success or failure, it's traditional to use a numeric result followed by the string value. Each string in the response strings list has an implied end-of-line transmitted back to the client:
procedure TForm1.IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
begin
if DoSomething then
ASender.Reply.SetReply(0,'OK')
else
ASender.Reply.SetReply(999,'ERROR');
end;
The idea with the IdCommandHandler and a CmdTCPServer/Client is that you follow the "RFC" style of protocols, which are ANSI/ASCII text-based. An RFC-style internet protocol's reply is typically encoded over the wire as text with both a numeric and string value. ASender.Response could be used if you needed to take the content of a string list and return that as the response.
As for the client, a question here suggests that TIdCmdTcpClient is not the most natural way to build the client for this server. From their names, you'd have thought they were made for each other, but it's not exactly. For most simple TIdCMDTCPServers that you could build, you would find that a plain-vanilla TIdTCPClient is the simplest building block to start your client with.

ZeroConf/Bonjour Code that works in Delphi 7 not working in 2009

I have the following declaration for DNSServiceRegister:
function DNSServiceRegister
(
var sdRef: TDNSServiceRef;
const flags: TDNSServiceFlags;
const interfaceIndex: uint32_t;
const name: PUTF8String; //* may be NULL */
const regType: PUTF8String;
const domain: PUTF8String; //* may be NULL */
const host: PUTF8String; //* may be NULL */
const port: uint16_t;
const txtLen: uint16_t;
const txtRecord: Pointer; //* may be NULL */
const callBack: TDNSServiceRegisterReply; //* may be NULL */
const context: Pointer //* may be NULL */
): TDNSServiceErrorType; stdcall; external DNSSD_DLL;
In my Bonjour framework I have the following response to an announced service being made active (i.e. to actually start announcing itself, via Bonjour):
procedure TAnnouncedService.Activate;
var
flags: Cardinal;
name: UTF8String;
svc: UTF8String;
pn: PUTF8String;
ps: PUTF8String;
begin
fPreAnnouncedServiceName := ServiceName;
inherited;
if AutoRename then
flags := 0
else
flags := kDNSServiceFlagsNoAutoRename; { - do not auto-rename }
if (ServiceName <> '') then
begin
name := ServiceName;
pn := PUTF8String(name);
end
else
pn := NIL;
svc := ServiceType;
ps := PUTF8String(svc);
CheckAPIResult(DNSServiceRegister(fHandle,
flags,
0 { interfaceID - register on all interfaces },
pn,
ps,
NIL { domain - register in all available },
NIL { hostname - use default },
ReverseBytes(Port),
0 { txtLen },
NIL { txtRecord },
DNSServiceRegisterReply,
self));
TBonjourEventHandler.Create(fHandle);
end;
This is more verbose than I think it strictly needs to be, certainly it was working perfectly well in Delphi 7 in a much less verbose form. I have expanded a lot of operations into explicit steps to facilitate debugging, e.g. to be able to identify any implicit transforms of string payloads that may be occuring "under the hood" in Delphi 2009.
Even in this untidy expanded form this code compiles and works perfectly well in Delphi 7, but if I compile and run with Delphi 2009 I get no announcement of my service.
For example, if I run this code as part of a Delphi 7 application to register a _daap._tcp service (an iTunes shared library) I see it pop-up in a running instance of iTunes. If I recompile the exact same application without modification in Delphi 2009 and run it, I do not see my service appearing in iTunes.
I get the same behaviour when monitoring with the dns-sd command line utility. That is, service code compiled with Delphi 7 behaves as I expect, compiled in Delphi 2009 - nothing.
I am not getting any errors from the Bonjour API - the DNSServiceRegisterReply callback is being called with an ErrorCode of 0 (zero), i.e. success, and if I supply a NIL name parameter with AutoRename specified in the flags then my service is allocated the correct default name. But still the service does not show up in iTunes.
I am at a loss as to what is going on.
As you might be able to tell from the expansion of the code, I have been chasing potential errors being introduced by the Unicode implementation in Delphi 2009, but this seems to be leading me nowhere.
The code was originally developed against version 1.0.3 of the Bonjour API/SDK. I've since updated to 1.0.6 in case that was somehow involved, without any success. afaict 1.0.6 merely added a new function for obtaining "properties", which currently supports only a "DaemonVersion" property for obtaining the Bonjour version - this is working perfectly.
NOTE: I'm aware that the code as it stands is not technically UTF8-safe in Delphi 7 - I have eliminated explicit conversions as far as possible so as to keep things as simple as possible for the automatic conversions that Delphi 2009 applies. My aim now is to get this working in Delphi 2009 then work backward from that solution to hopefully find a compatible approach for earlier versions of Delphi.
NOTE ALSO: I originally also had problems with browsing for advertised services, i.e. identifying an actual iTunes shared library on the network. Those issues were caused by the Unicode handling in Delphi 2009 and have been resolved. My Delphi 2009 code is just as capable of identifying an actual iTunes shared library and querying it's TXT records. It's only this service registration that isn't working.
I must be missing something stupid and obvious.
Does anyone have any ideas?!
UPDATE
Having returned to this problem I have now discovered the following:
If I have a pre-D2009 and a D2009+ IDE open (e.g D2006 and D2010) with the same project loaded into both IDE's concurrently:
Build and run under 2006: It works - my service announcement is picked up by iTunes
Switch to D2010 and run (without building): It does a minimal compile, runs and works.
Do a full build in D2010: It stops working
Switch back to D2006 and run (without building): It doesn't work
Do a full build in D2006: It works again
Does this give anyone any other ideas?
The answer to this is mind boggling. On the one hand I made a completely stupid, very simple mistake, but on the other hand it should never - as far as I can see - have worked in ANY version of Delphi!
The problem was nothing what-so-ever to do with the Unicode/non-unicodeness of any strings, but was actually due to a type mismatch in the PORT parameter.
I was passing in the result of ReverseBytes(Port) - that parameter expected a uint16_t, i.e. a Word value. My Port property was however declared (lazily) as an Integer!!
Once I fixed this and had Port declared as a Word, it now works on both D2007- and D2009+ versions of Delphi.
Very weird.
I can only think that some other edge-case behaviour of the compiler that might have somehow affected this was changed when Unicode support was introduced.
Based on the information that we have available here, the situation is this:
When calling the DLL with your code in Delphi 2007, it gives one result.
When calling the same DLL with your code in Delphi 2009, it gives another result.
The suspicion is, that it is related to the Delphi 2009 compiler.
Logically, the difference must therefore be, that Delphi 2009 sends different values as parameters. In order to make the debugging truly Delphi-independent, you therefore need to create a dummy DLL, which reports the values it gets. Other Delphi-dependent methods may be applied, like looking at the disassembly of the function-call into the DLL, and debugging it so that we know exactly what values are passed, and how, to the DLL, in both compilers.
I can't find the declaration instruction for the vars "ServiceName" and "ServiceType" in your code sample.
Assuming a String type (thus a unicode string), I guess (yes... no D2009 available to test this) lazy typecasting could be an issue:
name := ServiceName;
Why not use the following?
name := PAnsiChar(AnsiString(ServiceName))
Anyhow... just my 2 cts.
BTW:
I always use the pre defined "EmptyStr", "EmptyWideStr" ... so the test would look like:
if (ServiceName <> EmptyStr) then
which should be safe and avoid the confusion of types.
On the other side, Delphi may interpret the '' as an ANSIChar like the following declaration do:
const
MyParagraphChar = 'ยง';
Not sure... I'm confused - should go home now ;)
If the DLL is not written using Delphi 2009, you may want to use something else than PUTF8String. The Delphi 2009 Utf8String type is different from Delphi 2007's UTF8String type.
If the DLL was written using C/C++, I strongly suggest to use PAnsiChar() instead of PUtf8String.

How can I get the TNMHTTP Get method to respond on a redirect

I'm using TNMHTTP in Delphi to retrieve the code from a webpage. The code is relatively simple:
NMHTTP1 := TNMHTTP.Create(Self);
NMHTTP1.InputFileMode := FALSE;
NMHTTP1.OutputFileMode := FALSE;
NMHTTP1.ReportLevel := Status_Basic;
NMHTTP1.TimeOut := 3000;
URL := 'http://www....';
NMHTTP1.Get(URL);
S := NMHTTP1.Body;
I am catching exceptions in a try/except block, but that is not the problem.
The problem is that on executing the NMHTTP1.Get method when the URL is a redirect, that method does not return and the program hangs. This is despite the fact that I've put a timeout of 3000 seconds in.
So I see three possible ways of solving this (in order of easiest to hardest for me to modify my program):
Do whatever is necessary to get the NMHTTP1.Get method to respond.
Do some sort of check in advance of the NMHTTP1.Get statement to see if the URL is a redirect and get the URL it is redirecting to.
Use another method to get a webpage using Delphi. When I wrote this, I used Delphi 4 and did not have Indy. I now have Delphi 2009, so I would be willing to use something that works in it (maybe INDY) if a simple #1 or #2 answer is not available.
I would love to get an answer from someone that will work for me. Thanks in advance.
I would avoid the NetMasters controls, period.
Instead, you can use Indy's IdHTTP component, which has a RedirectMaximum property (defaults to 15) and an OnRedirect event in case you want to track the details.
I can advice you to switch to Indy. They are great for a lot of network protocols (with the exception of the IRC protocol). There are nice examples included so you can examine the working examples yourself.
Also have a look at http://www.indyproject.org/index.en.aspx for more information on indy.

Resources