Search code examples
delphiindyindy10

How to get a parsable response from SendCmd in Indy?


If I use TIdImap4.SendCmd to manually send an unsupported command to Indy, I'm a little bit confused how I retrieve the full response to manually parse it.

I'm sending the following command to manually request the Gmail labels for a message since this is not yet supported by Indy:

  IMAP.SendCmd(ImapCmdNum(),'UID FETCH '+uid+' (X-GM-LABELS)',['OK','BAD','NO'], false);

After calling this command, I checked my Indy Log file and it's successfully receiving an appropriate response from the server:

Sent 9/19/2015 11:10:40 AM: C5 UID FETCH 2385 (X-GM-LABELS)<EOL>
Recv 9/19/2015 11:10:40 AM: * 542 FETCH (X-GM-LABELS (testlabel) UID 2385)<EOL>C5 OK Success<EOL>

But now I can't seem to get any part of that response other than 'OK' from Indy. I've tried the following in the debugger and none of them have the raw response or anything else I could possibly manually parse:

IMAP.LastCmdResult = ('OK', $2521E60, nil, $2521EC0)
IMAP.LastCmdResult.Text = ()
IMAP.LastCmdResult.Code = 'OK'
IMAP.LastCmdResult.NumericCode = 0
IMAP.LastCmdResult.FormattedReply = ()

From the SendCmd documentation:

SendCmd is an overloaded function used to send the command specified in AOut to the peer connection.

SendCmd uses IOHandler to write the command in AOut to the peer connection.

AResponse indicates the response allowed for the command.

SendCmd calls GetResponse to determine if the response from the peer connection is allowed. If the response is not allowed, an exception is raised during processing in GetResponse.

When AResponse is contains -1, GetResponse is called with an empty array to indicate that any response code is permitted for the command. Otherwise, the value in AResponse is used to valid the response code.

Use LastCmdResult to access the numeric and text portions of the response for the command.

My understanding of this is that I should be using LastCmdResult to access various "portions of the response", but none of them have the raw response or any part of the response except "OK", so how do I get something parsable from the response to SendCmd?


Solution

  • The text you are looking for is, in fact, in the LastCmdResult.Text property. The debugger is not showing it to you, but that is where the label data is.

    As I told you 2 months ago in comments to my other answer that you linked to:

    Look at the implementation of TIdIMAP4.UIDRetrieveFlags(). It calls SendCmd() followed by ParseLastCmdResult() to parse the returned flags. You will have to replicate the same logic, substituting fdGmailLabels where fdFlags is currently being used (minus the call to ParseMessageFlagString() that is parsing the flags string to a TIdMessageFlagsSet).

    If you look at the implementation of TIdIMAP4.UIDRetrieveFlags() and then look at your code, you are not even calling SendCmd() correctly to begin with. You are passing the wrong value to the ATag parameter (unless ImapCmdNum() is simply calling TIdIMAP4.NewCmdCounter - TIdIMAP4 needs to generate the command counters so it can match them to the replies), and moe importantly you are passing the wrong values to the AExpectedResponses parameter.

    Try this instead (I tested it and it works):

    type
      TIdIMAP4Access = class(TIdIMAP4);
      TIdIMAPLineStructAccess = class(TIdIMAPLineStruct);
    
    var
      uid: string;
      labels: string;
    begin
      ...
      uid := ...;
      labels := '';
      IMAP.SendCmd('UID FETCH ' + uid + ' (X-GM-LABELS)', ['FETCH','UID']);
      if IMAP.LastCmdResult.Code = IMAP_OK then
      begin
        if IMAP.LastCmdResult.Text.Count > 0 then
        begin
          // The requested data is in IMAP.LastCmdResult.Text[0].
    
          // You can either parse it manually, or use the below
          // code to let TIdIMAP4 parse it for you...
    
          if TIdIMAP4Access(IMAP).ParseLastCmdResult(IMAP.LastCmdResult.Text[0], 'FETCH', ['X-GM-LABELS']) then begin
            labels := TIdIMAPLineStructAccess(TIdIMAP4Access(IMAP).FLineStruct).IMAPValue;
          end;
        end;
      end;
      ...
    end;