imap: Fetching headers is now working.
* No state is stored yet, though, so that all messages will be downloaded over and over again.
This commit is contained in:
@@ -135,7 +135,8 @@ public:
|
|||||||
|
|
||||||
// TODO: trigger download of mails for all messages below the
|
// TODO: trigger download of mails for all messages below the
|
||||||
// body fetch limit
|
// body fetch limit
|
||||||
IMAP::FetchCommand fetch(fFrom, fTo, IMAP::kFetchHeader);
|
IMAP::FetchCommand fetch(fFrom, fTo,
|
||||||
|
IMAP::kFetchHeader | IMAP::kFetchFlags);
|
||||||
fetch.SetListener(this);
|
fetch.SetListener(this);
|
||||||
|
|
||||||
status = protocol.ProcessCommand(fetch);
|
status = protocol.ProcessCommand(fetch);
|
||||||
@@ -145,9 +146,9 @@ public:
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t length)
|
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream, size_t& length)
|
||||||
{
|
{
|
||||||
fFetchStatus = fFolder.StoreTemporaryMessage(fetchFlags, stream,
|
fFetchStatus = fFolder.StoreMessage(fFile, fetchFlags, stream,
|
||||||
length, fRef);
|
length, fRef);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -155,7 +156,7 @@ public:
|
|||||||
virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags)
|
virtual void FetchedData(uint32 fetchFlags, uint32 uid, uint32 flags)
|
||||||
{
|
{
|
||||||
if (fFetchStatus == B_OK)
|
if (fFetchStatus == B_OK)
|
||||||
fFolder.StoreMessage(fetchFlags, uid, flags, fRef);
|
fFolder.MessageStored(fRef, fFile, fetchFlags, uid, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -164,6 +165,7 @@ private:
|
|||||||
uint32 fFrom;
|
uint32 fFrom;
|
||||||
uint32 fTo;
|
uint32 fTo;
|
||||||
entry_ref fRef;
|
entry_ref fRef;
|
||||||
|
BFile fFile;
|
||||||
status_t fFetchStatus;
|
status_t fFetchStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,76 @@
|
|||||||
#include <Node.h>
|
#include <Node.h>
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include "Commands.h"
|
||||||
|
|
||||||
|
#include "IMAPProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
static const char* kMailboxNameAttribute = "IMAP:mailbox";
|
static const char* kMailboxNameAttribute = "IMAP:mailbox";
|
||||||
static const char* kUIDValidityAttribute = "IMAP:uidvalidity";
|
static const char* kUIDValidityAttribute = "IMAP:uidvalidity";
|
||||||
static const char* kStateAttribute = "IMAP:state";
|
static const char* kStateAttribute = "IMAP:state";
|
||||||
|
static const char* kFlagsAttribute = "IMAP:flags";
|
||||||
static const char* kUIDAttribute = "MAIL:unique_id";
|
static const char* kUIDAttribute = "MAIL:unique_id";
|
||||||
|
|
||||||
|
|
||||||
IMAPFolder::IMAPFolder(const BString& mailboxName, const entry_ref& ref)
|
class TemporaryFile : public BFile {
|
||||||
|
public:
|
||||||
|
TemporaryFile(BFile& file)
|
||||||
|
:
|
||||||
|
fFile(file),
|
||||||
|
fDeleteFile(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~TemporaryFile()
|
||||||
|
{
|
||||||
|
if (fDeleteFile) {
|
||||||
|
fFile.Unset();
|
||||||
|
BEntry(fPath.Path()).Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Init(const BPath& path, entry_ref& ref)
|
||||||
|
{
|
||||||
|
int32 tries = 53;
|
||||||
|
while (true) {
|
||||||
|
BString name("temp-mail-");
|
||||||
|
name << system_time();
|
||||||
|
|
||||||
|
fPath = path;
|
||||||
|
fPath.Append(name.String());
|
||||||
|
|
||||||
|
status_t status = fFile.SetTo(fPath.Path(),
|
||||||
|
B_CREATE_FILE | B_FAIL_IF_EXISTS | B_READ_WRITE);
|
||||||
|
if (status == B_FILE_EXISTS && tries-- > 0)
|
||||||
|
continue;
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
fDeleteFile = true;
|
||||||
|
return get_ref_for_path(fPath.Path(), &ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeepFile()
|
||||||
|
{
|
||||||
|
fDeleteFile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BFile& fFile;
|
||||||
|
BPath fPath;
|
||||||
|
bool fDeleteFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
IMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
|
||||||
|
const entry_ref& ref)
|
||||||
:
|
:
|
||||||
|
fProtocol(protocol),
|
||||||
fRef(ref),
|
fRef(ref),
|
||||||
fMailboxName(mailboxName),
|
fMailboxName(mailboxName),
|
||||||
fUIDValidity(UINT32_MAX)
|
fUIDValidity(UINT32_MAX)
|
||||||
@@ -97,57 +158,66 @@ IMAPFolder::SetUIDValidity(uint32 uidValidity)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Stores the given \a stream into a temporary file using the provided
|
||||||
|
BFile object. A new file will be created, and the \a ref object will
|
||||||
|
point to it. The file will remain open when this method exits without
|
||||||
|
an error.
|
||||||
|
|
||||||
|
\a length will reflect how many bytes are left to read in case there
|
||||||
|
were an error.
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
IMAPFolder::StoreTemporaryMessage(uint32 fetchFlags, BDataIO& stream,
|
IMAPFolder::StoreMessage(BFile& file, uint32 fetchFlags, BDataIO& stream,
|
||||||
size_t length, entry_ref& ref)
|
size_t& length, entry_ref& ref)
|
||||||
{
|
{
|
||||||
BPath path;
|
BPath path;
|
||||||
status_t status = path.SetTo(&fRef);
|
status_t status = path.SetTo(&fRef);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
path.Append("tmp1234");
|
TemporaryFile temporaryFile(file);
|
||||||
|
status = temporaryFile.Init(path, ref);
|
||||||
BFile file;
|
|
||||||
status = (path.Path(), B_CREATE_FILE);
|
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
get_ref_for_path(path.Path(), &ref);
|
|
||||||
|
|
||||||
char buffer[65535];
|
char buffer[65535];
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
ssize_t bytesRead = stream.Read(buffer,
|
ssize_t bytesRead = stream.Read(buffer,
|
||||||
min_c(sizeof(buffer), length));
|
std::min(sizeof(buffer), length));
|
||||||
|
if (bytesRead < 0)
|
||||||
|
return bytesRead;
|
||||||
if (bytesRead <= 0)
|
if (bytesRead <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
file.Write(buffer, bytesRead);
|
|
||||||
length -= bytesRead;
|
length -= bytesRead;
|
||||||
|
|
||||||
|
ssize_t bytesWritten = file.Write(buffer, bytesRead);
|
||||||
|
if (bytesWritten < 0)
|
||||||
|
return bytesWritten;
|
||||||
|
if (bytesWritten != bytesRead)
|
||||||
|
return B_IO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
temporaryFile.KeepFile();
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Writes UID, and flags to the message, and notifies the protocol that a
|
||||||
|
message has been fetched. This method also closes the \a file passed in.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, uint32 flags,
|
IMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
|
||||||
entry_ref& ref)
|
uint32 uid, uint32 flags)
|
||||||
{
|
{
|
||||||
BString name = "mail-";
|
_WriteUniqueID(file, uid);
|
||||||
name << uid;
|
if ((fetchFlags & IMAP::kFetchFlags) != 0)
|
||||||
|
_WriteFlags(file, flags);
|
||||||
|
|
||||||
// TODO: remove renaming as its superfluous here
|
// TODO: the call below may move/rename the file
|
||||||
BEntry entry(&ref);
|
fProtocol.MessageStored(ref, file, fetchFlags);
|
||||||
entry.Rename(name.String());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
file.Unset();
|
||||||
status_t
|
|
||||||
IMAPFolder::StoreMessage(uint32 fetchFlags, uint32 uid, BDataIO& stream,
|
|
||||||
size_t length)
|
|
||||||
{
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -207,8 +277,8 @@ IMAPFolder::_InitializeFolderState()
|
|||||||
} else {
|
} else {
|
||||||
// This is a new message
|
// This is a new message
|
||||||
// TODO: the token must be the originating token!
|
// TODO: the token must be the originating token!
|
||||||
// TODO: uid might be udpated from the call
|
|
||||||
uid = fListener->MessageAdded(_Token(uid), ref);
|
uid = fListener->MessageAdded(_Token(uid), ref);
|
||||||
|
_WriteUniqueID(node, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
fRefMap.insert(std::make_pair(uid, ref));
|
fRefMap.insert(std::make_pair(uid, ref));
|
||||||
@@ -240,9 +310,31 @@ IMAPFolder::_ReadUniqueID(BNode& node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPFolder::_WriteUniqueID(BNode& node, uint32 uid)
|
||||||
|
{
|
||||||
|
BString string;
|
||||||
|
string << uid;
|
||||||
|
|
||||||
|
return node.WriteAttrString(kUIDAttribute, &string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
IMAPFolder::_ReadFlags(BNode& node)
|
IMAPFolder::_ReadFlags(BNode& node)
|
||||||
{
|
{
|
||||||
// TODO!
|
// TODO!
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
IMAPFolder::_WriteFlags(BNode& node, uint32 flags)
|
||||||
|
{
|
||||||
|
ssize_t bytesWritten = node.WriteAttr(kFlagsAttribute, B_UINT32_TYPE, 0,
|
||||||
|
&flags, sizeof(uint32));
|
||||||
|
if (bytesWritten == (ssize_t)sizeof(uint32))
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
return bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012, Axel Dörfler, [email protected].
|
* Copyright 2012-2013, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef IMAP_FOLDER_H
|
#ifndef IMAP_FOLDER_H
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
|
class IMAPProtocol;
|
||||||
|
|
||||||
|
|
||||||
struct MessageToken {
|
struct MessageToken {
|
||||||
BString mailboxName;
|
BString mailboxName;
|
||||||
uint32 uidValidity;
|
uint32 uidValidity;
|
||||||
@@ -36,7 +39,8 @@ public:
|
|||||||
|
|
||||||
class IMAPFolder : public BHandler {
|
class IMAPFolder : public BHandler {
|
||||||
public:
|
public:
|
||||||
IMAPFolder(const BString& mailboxName,
|
IMAPFolder(IMAPProtocol& protocol,
|
||||||
|
const BString& mailboxName,
|
||||||
const entry_ref& ref);
|
const entry_ref& ref);
|
||||||
virtual ~IMAPFolder();
|
virtual ~IMAPFolder();
|
||||||
|
|
||||||
@@ -45,13 +49,13 @@ public:
|
|||||||
void SetListener(FolderListener* listener);
|
void SetListener(FolderListener* listener);
|
||||||
void SetUIDValidity(uint32 uidValidity);
|
void SetUIDValidity(uint32 uidValidity);
|
||||||
|
|
||||||
status_t StoreTemporaryMessage(uint32 fetchFlags,
|
status_t StoreMessage(BFile& file, uint32 fetchFlags,
|
||||||
BDataIO& stream, size_t length,
|
BDataIO& stream, size_t& length,
|
||||||
entry_ref& ref);
|
entry_ref& ref);
|
||||||
void StoreMessage(uint32 fetchFlags, uint32 uid,
|
void MessageStored(entry_ref& ref, BFile& file,
|
||||||
uint32 flags, entry_ref& ref);
|
uint32 fetchFlags, uint32 uid,
|
||||||
status_t StoreMessage(uint32 fetchFlags, uint32 uid,
|
uint32 flags);
|
||||||
BDataIO& stream, size_t length);
|
|
||||||
void DeleteMessage(uint32 uid);
|
void DeleteMessage(uint32 uid);
|
||||||
void SetMessageFlags(uint32 uid, uint32 flags);
|
void SetMessageFlags(uint32 uid, uint32 flags);
|
||||||
|
|
||||||
@@ -61,12 +65,15 @@ private:
|
|||||||
void _InitializeFolderState();
|
void _InitializeFolderState();
|
||||||
const MessageToken _Token(uint32 uid) const;
|
const MessageToken _Token(uint32 uid) const;
|
||||||
uint32 _ReadUniqueID(BNode& node);
|
uint32 _ReadUniqueID(BNode& node);
|
||||||
|
status_t _WriteUniqueID(BNode& node, uint32 uid);
|
||||||
uint32 _ReadFlags(BNode& node);
|
uint32 _ReadFlags(BNode& node);
|
||||||
|
status_t _WriteFlags(BNode& node, uint32 flags);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
|
typedef std::hash_map<uint32, uint32> UIDToFlagsMap;
|
||||||
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
|
typedef std::hash_map<uint32, entry_ref> UIDToRefMap;
|
||||||
|
|
||||||
|
IMAPProtocol& fProtocol;
|
||||||
const entry_ref fRef;
|
const entry_ref fRef;
|
||||||
BString fMailboxName;
|
BString fMailboxName;
|
||||||
uint32 fUIDValidity;
|
uint32 fUIDValidity;
|
||||||
|
|||||||
@@ -126,6 +126,16 @@ IMAPProtocol::WorkerQuit(IMAPConnectionWorker* worker)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
IMAPProtocol::MessageStored(entry_ref& ref, BFile& stream, uint32 fetchFlags)
|
||||||
|
{
|
||||||
|
if ((fetchFlags & IMAP::kFetchHeader) != 0)
|
||||||
|
NotifyHeaderFetched(ref, &stream);
|
||||||
|
if ((fetchFlags & IMAP::kFetchBody) != 0)
|
||||||
|
NotifyBodyFetched(ref, &stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IMAPProtocol::SyncMessages()
|
IMAPProtocol::SyncMessages()
|
||||||
{
|
{
|
||||||
@@ -230,7 +240,7 @@ IMAPProtocol::_CreateFolder(const BString& mailbox, const BString& separator)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IMAPFolder(mailbox, ref);
|
return new IMAPFolder(*this, mailbox, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public:
|
|||||||
IMAP::Protocol& protocol, bool idle);
|
IMAP::Protocol& protocol, bool idle);
|
||||||
void WorkerQuit(IMAPConnectionWorker* worker);
|
void WorkerQuit(IMAPConnectionWorker* worker);
|
||||||
|
|
||||||
|
void MessageStored(entry_ref& ref, BFile& stream,
|
||||||
|
uint32 fetchFlags);
|
||||||
|
|
||||||
virtual status_t SyncMessages();
|
virtual status_t SyncMessages();
|
||||||
virtual status_t FetchBody(const entry_ref& ref);
|
virtual status_t FetchBody(const entry_ref& ref);
|
||||||
virtual status_t MarkMessageAsRead(const entry_ref& ref,
|
virtual status_t MarkMessageAsRead(const entry_ref& ref,
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ FetchCommand::HandleUntagged(Response& response)
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments,
|
FetchCommand::HandleLiteral(Response& response, ArgumentList& arguments,
|
||||||
BDataIO& stream, size_t length)
|
BDataIO& stream, size_t& length)
|
||||||
{
|
{
|
||||||
if (fListener == NULL || !response.EqualsAt(1, "FETCH")
|
if (fListener == NULL || !response.EqualsAt(1, "FETCH")
|
||||||
|| !response.IsListAt(2))
|
|| !response.IsListAt(2))
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ enum FetchFlags {
|
|||||||
class FetchListener {
|
class FetchListener {
|
||||||
public:
|
public:
|
||||||
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream,
|
virtual bool FetchData(uint32 fetchFlags, BDataIO& stream,
|
||||||
size_t length) = 0;
|
size_t& length) = 0;
|
||||||
virtual void FetchedData(uint32 fetchFlags, uint32 uid,
|
virtual void FetchedData(uint32 fetchFlags, uint32 uid,
|
||||||
uint32 flags) = 0;
|
uint32 flags) = 0;
|
||||||
};
|
};
|
||||||
@@ -166,7 +166,7 @@ public:
|
|||||||
virtual bool HandleUntagged(Response& response);
|
virtual bool HandleUntagged(Response& response);
|
||||||
virtual bool HandleLiteral(Response& response,
|
virtual bool HandleLiteral(Response& response,
|
||||||
ArgumentList& arguments, BDataIO& stream,
|
ArgumentList& arguments, BDataIO& stream,
|
||||||
size_t length);
|
size_t& length);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32 fFrom;
|
uint32 fFrom;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "Response.h"
|
#include "Response.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <UnicodeChar.h>
|
#include <UnicodeChar.h>
|
||||||
@@ -588,7 +589,7 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
|
|||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled && size <= 65536) {
|
||||||
// The default implementation just adds the data as a string
|
// The default implementation just adds the data as a string
|
||||||
TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size);
|
TRACE("Trying to read literal with %" B_PRIuSIZE " bytes.\n", size);
|
||||||
BString string;
|
BString string;
|
||||||
@@ -612,6 +613,9 @@ Response::ParseLiteral(ArgumentList& arguments, BDataIO& stream)
|
|||||||
|
|
||||||
string.UnlockBuffer(size);
|
string.UnlockBuffer(size);
|
||||||
arguments.AddItem(new StringArgument(string));
|
arguments.AddItem(new StringArgument(string));
|
||||||
|
} else {
|
||||||
|
// Skip any bytes left in the literal stream
|
||||||
|
_SkipLiteral(stream, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,6 +712,24 @@ Response::Read(BDataIO& stream)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Response::_SkipLiteral(BDataIO& stream, size_t size)
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
size_t totalRead = 0;
|
||||||
|
while (totalRead < size) {
|
||||||
|
size_t toRead = std::min(sizeof(buffer), size - totalRead);
|
||||||
|
ssize_t bytesRead = stream.Read(buffer, toRead);
|
||||||
|
if (bytesRead == 0)
|
||||||
|
throw ParseException("Unexpected end of literal");
|
||||||
|
if (bytesRead < 0)
|
||||||
|
throw StreamException(bytesRead);
|
||||||
|
|
||||||
|
totalRead += bytesRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
|
|
||||||
virtual bool HandleLiteral(Response& response,
|
virtual bool HandleLiteral(Response& response,
|
||||||
ArgumentList& arguments, BDataIO& stream,
|
ArgumentList& arguments, BDataIO& stream,
|
||||||
size_t length) = 0;
|
size_t& length) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -168,6 +168,9 @@ protected:
|
|||||||
char Peek(BDataIO& stream);
|
char Peek(BDataIO& stream);
|
||||||
char Read(BDataIO& stream);
|
char Read(BDataIO& stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _SkipLiteral(BDataIO& stream, size_t size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LiteralHandler* fLiteralHandler;
|
LiteralHandler* fLiteralHandler;
|
||||||
uint32 fTag;
|
uint32 fTag;
|
||||||
|
|||||||
Reference in New Issue
Block a user