use of dev.hawala.xns.level4.common.AuthChsCommon.Verifier in project dodo by devhawala.
the class MailingNewImpl method transport5_postBegin.
/*
* postBegin
* = procedure 1
*/
private static void transport5_postBegin(PostBeginParams params, PostBeginResults results) {
// log ingoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
params.append(sb, " ", "params");
logf("##\n## procedure MailingNewImpl.transport5_postBegin() -- params\n%s\n##\n", sb.toString());
}
// check the credentials:
// - this procedure is called for the generic mail server ("Mail Service:CHServers:CHServers")
// - not for this specific mail service (which the 1st mail service name in the clearinghouse database)
// - so use the generic nameconversationKey
// - and: only the machine id for *this* mail service works...
Credentials credentials = params.getCredentials();
Verifier verifier = params.getVerifier();
StrongVerifier decodedVerifier = StrongVerifier.make();
int[] decodedConversationKey = new int[4];
ThreePartName senderName = // throws an exception on invalid credentials
mailService.checkCredentials(mailService.getChsDatabase().getGenericMailServiceName(), mailService.getMachineId(), credentials, verifier, decodedConversationKey, decodedVerifier);
// start the mail transaction for this post call sequence
PostMailTransaction mailTransaction = createMailTransaction();
// just to be sure we have some sender name
mailTransaction.senderName = senderName;
// set return values
results.mailTransaction.set(mailTransaction.transactionId);
if (credentials.type.get() == CredentialsType.simple) {
// return the initiators verifier
results.verifier.add().set(verifier.get(0).get());
} else {
// create a strong verifier based on the received verifier
// session.getConversationKey();
int[] conversationKey = decodedConversationKey;
if (conversationKey != null && conversationKey.length == 4) {
// xor-ing values
// the server machine, not(!) the remoteHostId extracted from the Logon request
long xorHostId = machineId;
// left justified machine-id => upper 32 bits
long rcptTimestampMachineId32Bits = (xorHostId >> 16) & 0xFFFFFFFFL;
// left justified machine-id => lower 32 bits
long rcptTicksMachineId32Bits = (xorHostId & 0x0000FFFFL) << 16;
// new verifier values
long newTicks = decodedVerifier.ticks.get() + 1;
long newTimestamp = decodedVerifier.timeStamp.get();
if (newTicks > 0xFFFFFFFFL) {
newTicks = 0;
newTimestamp++;
}
// plain (unencrypted) verifier with xor-ed values
StrongVerifier verfr = StrongVerifier.make();
verfr.ticks.set(newTicks ^ rcptTicksMachineId32Bits);
verfr.timeStamp.set(newTimestamp ^ rcptTimestampMachineId32Bits);
// encrypt verifier and transfer into results
try {
WireWriter writer = new WireWriter();
verfr.serialize(writer);
int[] sourceBytes = writer.getWords();
int[] encrypted = StrongAuthUtils.xnsDesEncrypt(conversationKey, sourceBytes);
for (int i = 0; i < encrypted.length; i++) {
results.verifier.add().set(encrypted[i]);
}
} catch (Exception e) {
// log and set no verifier => let the invoker decide if acceptable
logf("** !! unable to serialize or encrypt the verifier in logon results: " + e.getMessage());
}
}
}
results.unknown0.set(0);
// log outgoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
results.append(sb, " ", "results");
logf("##\n## procedure MailingNewImpl.transport5_postBegin() -- results\n%s\n##\n", sb.toString());
}
}
use of dev.hawala.xns.level4.common.AuthChsCommon.Verifier in project dodo by devhawala.
the class MailingNewImpl method inbasket2_getNextMail.
private static void inbasket2_getNextMail(GetNextMailParams params, GetNextMailResults results) {
// logging callback used before returning
UnaryOperator<String> retlog = m -> {
if (logParamsAndResults) {
if (m == null) {
m = "";
}
StringBuilder sb = new StringBuilder();
results.append(sb, " ", "results");
logf("##\n## procedure MailingNewImpl.inbasket2_getNextMail() %s -- results\n%s\n##\n", m, sb.toString());
}
return null;
};
// log ingoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
params.append(sb, " ", "params");
logf("##\n## procedure MailingNewImpl.inbasket2_getNextMail() -- params\n%s\n##\n", sb.toString());
}
// get the session (we do not check the verifier, as we trust our clients...)
dlogf(" ... fetching mail session ...");
MailSession session = mailService.getSession(params.sessionId.get());
if (session == null) {
logf("failed (invalid session id)\n");
retlog.apply("[invalid mail session]");
return;
}
// check for valid index for the mail in the inbox
MailData prevMailData = session.getClientData();
int index = (prevMailData == null) ? 0 : prevMailData.mailboxIndex + 1;
dlogf(" ... mailIndex: %d\n", index);
if (index >= session.getMailCount() || index < 0) {
// not or no longer present => return empty results
retlog.apply("[no more mails available]");
return;
}
// get the mail content
dlogf(" ... getting mailbox entry ... ");
MailboxEntry me = session.getMailEntry(index);
dlogf("%s\n", (me != null) ? "ok" : "failed (null)");
if (me == null) {
// not or no longer present => return empty results
retlog.apply("[mail already deleted]");
return;
}
dlogf(" ... loading mail content file ... ");
ByteArrayOutputStream mailBos = new ByteArrayOutputStream();
try {
me.transferContent(istream -> {
byte[] buffer = new byte[512];
try {
int count = istream.read(buffer);
while (count > 0) {
mailBos.write(buffer, 0, count);
count = istream.read(buffer);
}
} catch (IOException e) {
// ignore that for now
}
});
} catch (IOException e) {
// we must at least be able to read the mail file, so...
retlog.apply("[unable to read mail content]");
return;
}
byte[] mailContentBytes = mailBos.toByteArray();
dlogf("%d bytes\n", mailContentBytes.length);
// extract the relevant fields from the (old format) mail content
dlogf(" ... deserializing mail content ... ");
SerializedFile mailContentFile = SerializedFile.make();
try {
mailContentFile.deserialize(new ByteArrayWireInputStream(mailContentBytes));
} catch (EndOfMessageException e) {
// invalid mail file content??
retlog.apply("[invalid mail file content]");
return;
}
dlogf("ok\n");
dlogf(" ... extracting relevant data\n");
NameList xfrom = null;
NameList xto = null;
NameList xcc = null;
NameList xreplyTo = null;
STRING xsubject = null;
STRING mailText = null;
boolean xisFolderAttachment = false;
SEQUENCE<Attribute> attrs = mailContentFile.file.attributes.value;
for (int i = 0; i < attrs.size(); i++) {
try {
Attribute attr = attrs.get(i);
int attributeType = (int) (attr.type.get() & 0xFFFF_FFFFL);
switch(attributeType) {
case MailingCommon.from:
xfrom = attr.decodeData(NameList::make);
dlogf(" ... found from\n");
break;
case MailingCommon.to:
xto = attr.decodeData(NameList::make);
dlogf(" ... found to\n");
break;
case MailingCommon.cc:
xcc = attr.decodeData(NameList::make);
dlogf(" ... found cc\n");
break;
case MailingCommon.replyTo:
xreplyTo = attr.decodeData(NameList::make);
dlogf(" ... found replyTo\n");
break;
case MailingCommon.subject:
xsubject = attr.decodeData(STRING::make);
dlogf(" ... found subject\n");
break;
case MailingCommon.comments:
mailText = attr.decodeData(STRING::make);
dlogf(" ... found comments\n");
break;
case FilingCommon.atIsDirectory:
xisFolderAttachment = attr.getAsBoolean();
dlogf(" ... found isDirectory\n");
break;
}
} catch (EndOfMessageException e) {
// invalid mail file content??
retlog.apply("[unable to extract attribute mail file content]");
return;
}
}
dlogf(" ... done extracting mail content data\n");
if (xfrom == null || xfrom.size() == 0 || xto == null || xto.size() == 0) {
retlog.apply("[missing one of from, to]");
return;
}
// extract the relevant fields from the (old format) mail envelope
dlogf(" ... loading old-format envelope ... ");
ByteArrayOutputStream envBos = new ByteArrayOutputStream();
try {
me.transferPostboxEnvelope(istream -> {
byte[] buffer = new byte[512];
try {
int count = istream.read(buffer);
while (count > 0) {
envBos.write(buffer, 0, count);
count = istream.read(buffer);
}
} catch (IOException e) {
// ignore that for now
}
});
} catch (IOException e) {
// we must at least be able to read the mail envelope, so...
retlog.apply("[unable to read mail envelope]");
return;
}
byte[] oldEnvelopeBytes = envBos.toByteArray();
dlogf("ok -> %d bytes\n", oldEnvelopeBytes.length);
dlogf(" ... deserializing old-format envelope ... ");
EncodedList oldEnvelope = EncodedList.make();
try {
oldEnvelope.deserialize(new ByteArrayWireInputStream(oldEnvelopeBytes));
} catch (EndOfMessageException e) {
// invalid mail file envelope??
retlog.apply("[invalid mail file envelope]");
return;
}
dlogf("ok\n");
StringBuilder envSb = new StringBuilder();
oldEnvelope.append(envSb, " ", "old-envelope");
dlogf("+++ %s\n", envSb.toString());
dlogf(" ... extracting mail-id ... ");
MessageID xmailId = null;
for (int i = 0; i < oldEnvelope.size(); i++) {
Attribute attr = oldEnvelope.get(i);
if (attr.type.get() == MailingCommon.atMtMessageID) {
try {
xmailId = attr.decodeData(MessageID::make);
} catch (EndOfMessageException e) {
// invalid mail file envelope??
retlog.apply("[cannot decode messageId from mail file envelope]");
return;
}
}
}
if (xmailId == null) {
// invalid mail file envelope??
retlog.apply("[invalid mail file envelope]");
return;
}
dlogf("ok\n");
MessageID mailId = xmailId;
// synthesize a new format envelope from the old format mail data
dlogf(" ... creating new-format envelope\n");
NameList from = xfrom;
NameList to = xto;
NameList cc = xcc;
NameList replyTo = xreplyTo;
STRING subject = (xsubject == null) ? STRING.make() : xsubject;
boolean isFolderAttachment = xisFolderAttachment;
// long mailCreatedOn = me.inboxEntry().getCreatedOn();
SEQUENCE<Attribute> newEnvelope = new SEQUENCE<>(Attribute::make);
newEnvelope.add().set(MailTransport5.atSenderAndDate, ThreePartNameWithTagAndDateString::make, v -> {
v.nameWithTag.name.from(from.get(0));
// check where this thing possibly re-appears in GlobalView and if so implement as non-constant
v.date.set("24-Jan-94 21:40:54");
});
newEnvelope.add().set(MailTransport5.atSenderA, ThreePartNameWithTag::make, v -> {
v.name.from(from.get(0));
});
newEnvelope.add().set(MailTransport5.atSenderB, ListOfThreePartNameWithTag::make, v -> {
copyNames(from, v);
});
newEnvelope.add().set(MailTransport5.atTo, ListOfThreePartNameWithTag::make, v -> {
copyNames(to, v);
});
if (cc != null && cc.size() > 0) {
newEnvelope.add().set(MailTransport5.atCopiesTo, ListOfThreePartNameWithTag::make, v -> {
copyNames(cc, v);
});
}
if (replyTo != null && replyTo.size() > 0) {
newEnvelope.add().set(MailTransport5.atReplyTo, ListOfThreePartNameWithTag::make, v -> {
copyNames(replyTo, v);
});
}
newEnvelope.add().set(MailTransport5.atSubject, STRING::make, v -> v.set(subject));
dlogf(" ... done\n");
// prepare the temp object holding the contents data to be delivered later
// and create the serialized data for later use
MailData mailData = new MailData(((long) session.getSessionId() & 0x0000_0000_FFFF_FFFFL) + index, index);
dlogf(" ... serializing new-format envelope ...");
WireWriter wireWriter = new WireWriter();
try {
newEnvelope.serialize(wireWriter);
} catch (NoMoreWriteSpaceException e) {
// should never happen => ignore
}
mailData.envelopeBytes = wireWriter.getBytes();
dlogf("ok, %d bytes\n", mailData.envelopeBytes.length);
if (mailText != null && mailText.get() != null) {
dlogf(" ... creating mail-text (comments) bytes ... ");
wireWriter = new WireWriter();
try {
mailText.serialize(wireWriter);
} catch (NoMoreWriteSpaceException e) {
// should never happen => ignore
}
// skip the length word
byte[] mailtextBytes = wireWriter.getBytes(2);
if (mailtextBytes.length > 0) {
if (mailtextBytes[mailtextBytes.length - 1] == 0x00) {
mailtextBytes[mailtextBytes.length - 1] = ' ';
}
mailData.mailtextBytes = mailtextBytes;
dlogf("ok, %d bytes\n", mailData.mailtextBytes.length);
} else {
dlogf(" empty mail-text, mail part NOT created\n");
}
}
if (mailContentFile.file.content.data.size() > 0 || xisFolderAttachment) {
mailData.attachmentBytes = mailContentBytes;
dlogf(" ... added mail attachment, %d bytes\n", mailData.attachmentBytes.length);
}
// prepare the mailInfo-Attributes in the return object
dlogf(" ... building mail-infos for return data\n");
dlogf(" ... miMailServer ... ");
results.mailInfos.value.add().set(Inbasket2.miMailServer, MiMailServer::make, v -> {
v.name.from(mailService.getServiceName());
// obscure ...
v.unknown1.set(0xA926);
// ... constants?
v.unknown2.set(0xEB6D);
});
dlogf("ok\n");
dlogf(" ... miMessageId ... ");
results.mailInfos.value.add().set(Inbasket2.miMessageId, MailingCommon.MessageID::make, v -> {
for (int i = 0; i < 5; i++) {
v.get(i).set(mailId.get(i).get());
}
});
dlogf("ok\n");
dlogf(" ... miWhatever ... ");
results.mailInfos.value.add().set(Inbasket2.miWhatever, MiWhatever::make, v -> {
v.value.set(4);
});
dlogf("ok\n");
dlogf(" ... miMailparts ... ");
long[] lengthSum = { 0 };
results.mailInfos.value.add().set(Inbasket2.miMailparts, MiMailParts::make, v -> {
// envelope (always present)
MiMailPart envPart = v.add();
envPart.mailPartType.set(MailTransport5.mptEnvelope);
envPart.mailPartLength.set(mailData.envelopeBytes.length);
lengthSum[0] = mailData.envelopeBytes.length;
mailData.parts.add(mailData.envelopeBytes);
dlogf("envelope ");
// having mail-text?
if (mailData.mailtextBytes != null) {
MiMailPart mailTextPart = v.add();
mailTextPart.mailPartType.set(MailTransport5.mptNoteGV);
mailTextPart.mailPartLength.set(mailData.mailtextBytes.length);
lengthSum[0] += mailData.mailtextBytes.length;
mailData.parts.add(mailData.mailtextBytes);
dlogf("noteGV ");
}
// having mail-attachment?
if (mailData.attachmentBytes != null) {
MiMailPart mailTextPart = v.add();
mailTextPart.mailPartType.set(isFolderAttachment ? MailTransport5.mptAttachmentFolder : 4);
mailTextPart.mailPartLength.set(mailData.attachmentBytes.length);
lengthSum[0] += mailData.attachmentBytes.length;
mailData.parts.add(mailData.attachmentBytes);
dlogf("attachment(%d) ", mailTextPart.mailPartType.get());
}
});
dlogf("... ok\n");
dlogf(" ... miTotalPartsLength ... ");
results.mailInfos.value.add().set(Inbasket2.miTotalPartsLength, MiTotalPartsLength::make, v -> {
v.totalLength.set(lengthSum[0]);
});
dlogf("ok\n");
dlogf(" ... miUser0 ... ");
results.mailInfos.value.add().set(Inbasket2.miSender0, MiSender::make, v -> {
v.senderName.from(from.get(0));
});
dlogf("ok\n");
dlogf(" ... miUser1 ... ");
results.mailInfos.value.add().set(Inbasket2.miSender1, MiSender::make, v -> {
v.senderName.from(from.get(0));
});
dlogf("ok\n");
dlogf(" ... done\n");
// fill the obscure array values in the return data
dlogf(" ... adding obscure array ... ");
if (mailData.mailtextBytes != null && mailData.attachmentBytes != null) {
// all 3 mail parts
int[] vals = { 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001 };
for (int i = 0; i < vals.length; i++) {
results.unknownSeq.add().set(vals[i]);
}
} else if (mailData.mailtextBytes == null && mailData.attachmentBytes == null) {
// only 1 mail part (envelope)
int[] vals = { 0x0000, 0x0001, 0x0000, 0x0000 };
for (int i = 0; i < vals.length; i++) {
results.unknownSeq.add().set(vals[i]);
}
} else {
// 2 mail parts (envelope and mail-text or attachment)
int[] vals = { 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };
for (int i = 0; i < vals.length; i++) {
results.unknownSeq.add().set(vals[i]);
}
}
dlogf("%d words\n", results.unknownSeq.size());
// done: remember the data container with the mail-parts for later transfer and add the id to the returned data
session.setClientData(mailData);
results.uniqueMailNo.set(mailData.mailTransferId);
dlogf(" ... uniqueMailNo: 0x%08X\n", results.uniqueMailNo.get());
// log outgoing data
retlog.apply(null);
}
use of dev.hawala.xns.level4.common.AuthChsCommon.Verifier in project dodo by devhawala.
the class MailingNewImpl method inbasket2_getMailPart.
/*
* getMailPart
* = procedure 8
* (1 or more mail part content(s) are sent via bulk-data transfer to the invoker)
*/
private static void inbasket2_getMailPart(GetMailPartParams params, RECORD results) {
// log ingoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
params.append(sb, " ", "params");
logf("##\n## procedure MailingNewImpl.inbasket2_getMailPart() -- params\n%s\n##\n", sb.toString());
}
// get the session (we do not check the verifier, as we trust our clients...)
MailSession session = mailService.getSession(params.sessionId.get());
// check if we are working with the current mail-data
MailData mailData = session.getClientData();
long requestedTransferId = params.uniqueMailNo.get();
dlogf(" ... requestedTransferId = %d , currentTransferId = %d\n", requestedTransferId, mailData.mailTransferId);
if (mailData.mailTransferId != requestedTransferId) {
throw new IllegalStateException("invalid mail-transfer-id (protocol exception?)");
}
// send the requested mail part(s)
try {
dlogf(" ... begin sending bulk-data\n");
ByteContentSink sink = new ByteContentSink(params.content);
try {
for (int i = 0; i < params.mailPartIndices.size(); i++) {
int idx = params.mailPartIndices.get(i).get();
if (idx >= 0 && idx < mailData.parts.size()) {
byte[] partBytes = mailData.parts.get(idx);
dlogf(" ... sending mailPart[ %d ] , length: %d bytes\n", idx, partBytes.length);
sink.write(partBytes, partBytes.length);
}
}
} finally {
dlogf(" ... closing bulk channel\n");
// signal EOF and cleanup
sink.write(null, 0);
}
} catch (Exception e) {
logf(" +++ error: %s - %s\n", e.getClass().getName(), e.getMessage());
}
dlogf(" ... finished sending bulk-data\n");
// log outgoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
results.append(sb, " ", "results");
logf("##\n## procedure MailingNewImpl.inbasket2_getMailPart() -- results\n%s\n##\n", sb.toString());
}
}
use of dev.hawala.xns.level4.common.AuthChsCommon.Verifier in project dodo by devhawala.
the class MailingOldImpl method transport_post.
/*
* post
* = procedure 1
*/
private static void transport_post(PostParams params, PostResults results) {
// log ingoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
params.append(sb, " ", "params");
log("##\n## procedure MailingImpl.transport_post() -- params\n%s\n##\n", sb.toString());
}
// check the credentials:
// - this procedure is called for the generic mail server ("Mail Service:CHServers:CHServers")
// - not for this specific mail service (which the 1st mail service name in the clearinghouse database)
// - so use the generic name
// - and: only the machine id for *this* mail service works...
Credentials credentials = params.authPair.credentials;
Verifier verifier = params.authPair.verifier;
StrongVerifier decodedVerifier = StrongVerifier.make();
int[] decodedConversationKey = new int[4];
ThreePartName senderName = // throws an exception on invalid credentials
mailService.checkCredentials(mailService.getChsDatabase().getGenericMailServiceName(), mailService.getMachineId(), credentials, verifier, decodedConversationKey, decodedVerifier);
// check the recipients
NameList allRecipients = NameList.make();
ChsDatabase chs = mailService.getChsDatabase();
for (int i = 0; i < params.recipients.size(); i++) {
Name rcpt = params.recipients.get(i);
String rcptFqn = chs.resolveName(rcpt);
List<Name> dlMemberNames;
if (rcptFqn != null && mailService.hasMailbox(rcptFqn)) {
Name rcptName = Name.make();
rcptName.from(rcptFqn);
allRecipients.addDistinct(rcptName);
} else if (params.allowDLRecipients.get() && rcptFqn != null && (dlMemberNames = getUserGroupMembersLcFqns(rcptFqn)) != null) {
for (Name dlMember : dlMemberNames) {
allRecipients.addDistinct(dlMember);
}
} else {
UndeliveredName undelivered = UndeliveredName.make();
undelivered.reason.set(UndeliveredNameType.noSuchRecipient);
undelivered.name.from(rcpt);
results.invalidNames.add(undelivered);
}
}
// if invalid recipients are not allowed and we have some or if all recipients are invalid: throw error...
if ((results.invalidNames.size() > 0 && !params.postIfInvalidNames.get()) || results.invalidNames.size() == params.recipients.size()) {
InvalidRecipientsErrorRecord err = new InvalidRecipientsErrorRecord();
for (int i = 0; i < results.invalidNames.size(); i++) {
err.nameList.add(results.invalidNames.get(i));
}
err.raise();
}
// so create the mail
try {
ByteContentSource source = new ByteContentSource(params.content);
if (allRecipients.size() > 0) {
int[] mailId = mailService.postMail(senderName, allRecipients, params.contentsType.get(), source);
for (int i = 0; i < mailId.length; i++) {
results.msgID.get(i).set(mailId[i]);
}
} else {
// abort bulk-data transfer
source.read(null);
}
} catch (EndOfMessageException e) {
new ConnectionErrorRecord(ConnectionProblem.otherCallProblem).raise();
}
// log outgoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
results.append(sb, " ", "results");
log("##\n## procedure MailingImpl.transport_post() -- results\n%s\n##\n", sb.toString());
}
}
use of dev.hawala.xns.level4.common.AuthChsCommon.Verifier in project dodo by devhawala.
the class MailingOldImpl method inbasket_inbasketPoll.
/*
* inbasketPoll
* = procedure 7
*/
private static void inbasket_inbasketPoll(InbasketPollParams params, InbasketPollResults results) {
// log ingoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
params.append(sb, " ", "params");
log("##\n## procedure MailingImpl.inbasket_inbasketPoll() -- params\n%s\n##\n", sb.toString());
}
// check user credentials
Credentials credentials = params.mbx.creds.credentials;
Verifier verifier = params.mbx.creds.verifier;
StrongVerifier decodedVerifier = StrongVerifier.make();
int[] decodedConversationKey = new int[4];
// throws an exception on invalid credentials
mailService.checkCredentials(mailService.getServiceName(), mailService.getMachineId(), credentials, verifier, decodedConversationKey, decodedVerifier);
// check if the mailbox is available
Name reqMbxName = params.mbx.name;
// mbxName.getLcFqn();
String mailboxFqn = mailService.getChsDatabase().resolveName(reqMbxName);
if (mailboxFqn == null || !mailService.hasMailbox(mailboxFqn)) {
new AccessErrorRecord(AccessProblem.noSuchMailbox).raise();
}
log("## Inbasket.poll for mailbox: %s\n", mailboxFqn);
Name mbxName = Name.make();
mbxName.from(mailboxFqn);
// get the status data
mailService.getMailboxState(mbxName, results.pollState);
// log outgoing data
if (logParamsAndResults) {
StringBuilder sb = new StringBuilder();
results.append(sb, " ", "results");
log("##\n## procedure MailingImpl.inbasket_inbasketPoll() -- results\n%s\n##\n", sb.toString());
}
}
Aggregations