According to support threads, Indy’s TIdImap4 supports fetching Gmail labels but doesn’t have support built in for adding and removing labels. I’m still working on figuring out how to fetch and read the fetched labels, but by using the SendCmd function to manually send the IMAP commands I got adding and removing labels working. So I thought I’d share that code:
function AddGmailLabelToMsgs(const uidList: TStrings; labelname : string) : boolean; begin try if HasCapa('X-GM-EXT-1') and (uidList.Count > 0) and (labelname <> '') then begin IMAP.SendCmd(ImapCmdNum(),'UID STORE '+uidList.CommaText+' +X-GM-LABELS ("'+ labelname + '")',['OK','BAD','NO'], true); Result := IMAP.LastCmdResult.Code = 'OK'; end else Result := false; except on E : Exception do begin //Dialogs.ShowMessage('Exception class name = '+E.ClassName); //Dialogs.ShowMessage('Exception message = '+E.Message); Result := false; end; end; end;
At the heart of it, I am using Indy’s SendCmd function to manually send the command. It was a little tricky figuring out how to use this function since documentation and examples are a little scarce. The first parameter is a sequence number so that the response can be matched up with the command. Second is the remainder of the actual command. Third parameter is what responses to look for to know this command is “complete”, and the fourth is whether to look for a single line response or multi-line response.
I also have some commented out exception handling that was helpful to debugging when my SendCmd wasn’t formatted correctly, but is neither needed nor useful for production code.
Remove label is basically the same thing but with a minus sign:
function RemoveGmailLabelFromMsgs(const uidList: TStrings; labelname : string): boolean; begin try if HasCapa('X-GM-EXT-1') and (uidList.Count >0) and (labelname <> '') then begin IMAP.SendCmd(ImapCmdNum(),'UID STORE '+uidList.CommaText+' -X-GM-LABELS ("'+ labelname + '")',['OK','BAD','NO'], true); Result := IMAP.LastCmdResult.Code = 'OK'; end else Result := false; except Result := false; end; end;
And here’s the helper functions, minus the global/class/whatever variables:
ImapCmdNum() generates a sequence number for the IMAP transaction, essentially a replacement for NewCmdCounter in IdImap4, since it’s a protected property and client code can’t use it. At first I tried using “A1”, “A2”, etc. but it turned out that using a C prefix, “C1″, C2”, etc. like Indy was important for using Indy to process the response when I got into debugging why things weren’t working right at first…but now I’ve completely forgot exactly where it goes wrong if you don’t use a C.
function ImapCmdNum(): string; begin Result := 'C'+IntToStr(cmdNum); inc(cmdNum) end;
And HasCapa() is a function I added to make sure that the Gmail capabilities are actually available before trying to set/unset labels…Might not be totally necessary, but it seemed like a good idea. Under the hood, I just used Indy’s built in Capability function that calls CAPABILITY on the server, with a little caching so I wouldn’t need to do this more than once per session.
function TProtocolIMAP4.HasCapa(capability: string) : boolean; begin if (capabilities.count = 0) then begin IMAP.Capability(capabilities); end; Result := (capabilities.IndexOf(capability)<>-1); end;