Leave an unknown notation alone and read a receipt's TLV as the id the submit answered
This commit is contained in:
+1
-1
@@ -168,7 +168,7 @@ async function connect(options: ClientOptions, log: SmppLog): Promise<Result<{ s
|
||||
const checked = checkSessionOptions(options);
|
||||
|
||||
if (checked.err) {
|
||||
log.warn('client - option out of range', { message: checked.err.message });
|
||||
log.warn('client - unusable option', { message: checked.err.message });
|
||||
|
||||
return { err: checked.err };
|
||||
}
|
||||
|
||||
+11
-6
@@ -1,7 +1,7 @@
|
||||
import type { MessageState } from './defs/constants.ts';
|
||||
import type { ParamValue } from './defs/types.ts';
|
||||
import type { PduObject } from './pdu.ts';
|
||||
import type { SmsIdNotation } from './sms-id.ts';
|
||||
import type { SmsIdFormat } from './sms-id.ts';
|
||||
import { consts, constsById, messageTypeOf } from './defs/constants.ts';
|
||||
import { decodeMessage } from './message.ts';
|
||||
import { normaliseSmsId } from './sms-id.ts';
|
||||
@@ -164,11 +164,16 @@ function receiptBody(pduObj: PduObject): string {
|
||||
function receiptId(
|
||||
tlvId: ParamValue | undefined,
|
||||
receipt: Receipt | undefined,
|
||||
notation: SmsIdNotation | undefined,
|
||||
format: SmsIdFormat,
|
||||
): string | undefined {
|
||||
const id = nonEmptyText(tlvId) ?? nonEmptyText(receipt?.id);
|
||||
// SMPP 3.4 5.3.2.26: the TLV is the id the submit_sm_resp carried, where the body's is a rendering.
|
||||
const fromTlv = nonEmptyText(tlvId);
|
||||
|
||||
return id === undefined ? undefined : normaliseSmsId(id, notation);
|
||||
if (fromTlv !== undefined) return normaliseSmsId(fromTlv, format.submitResp);
|
||||
|
||||
const fromBody = nonEmptyText(receipt?.id);
|
||||
|
||||
return fromBody === undefined ? undefined : normaliseSmsId(fromBody, format.receipt);
|
||||
}
|
||||
|
||||
function isMessageState(name: string | undefined): name is MessageState {
|
||||
@@ -192,14 +197,14 @@ function receiptStatus(
|
||||
}
|
||||
|
||||
/** The delivery report a deliver_sm carries, or nothing when it carries a message instead. */
|
||||
export function dlrFromPdu(pduObj: PduObject, notation?: SmsIdNotation): Dlr | undefined {
|
||||
export function dlrFromPdu(pduObj: PduObject, format: SmsIdFormat = {}): Dlr | undefined {
|
||||
const type = messageType(pduObj);
|
||||
|
||||
if (type === 'other') return undefined;
|
||||
|
||||
const body = receiptBody(pduObj);
|
||||
const receipt = body === '' ? undefined : parseReceipt(body);
|
||||
const smsId = receiptId(pduObj.tlvs.receipted_message_id?.tagValue, receipt, notation);
|
||||
const smsId = receiptId(pduObj.tlvs.receipted_message_id?.tagValue, receipt, format);
|
||||
const { statusId, statusMsg } = receiptStatus(pduObj.tlvs.message_state?.tagValue, receipt);
|
||||
|
||||
if (type === 'unmarked' && (smsId === undefined || statusMsg === undefined)) return undefined;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { OnRequest } from './session-options.ts';
|
||||
import type { PduObject } from './pdu.ts';
|
||||
import type { Session } from './session.ts';
|
||||
import type { SmppLog } from './log.ts';
|
||||
import type { SmsIdNotation } from './sms-id.ts';
|
||||
import type { SmsIdFormat } from './sms-id.ts';
|
||||
import { Reassembler, decodeSegments } from './reassembly.ts';
|
||||
import { bindCommands, defaults } from './session-options.ts';
|
||||
import { concatInfo } from './udh.ts';
|
||||
@@ -19,7 +19,7 @@ export type IncomingRequestsOptions = {
|
||||
maxReassembly?: number | undefined;
|
||||
onRequest?: OnRequest | undefined;
|
||||
reassemblyTimeout?: number | undefined;
|
||||
receiptIdNotation?: SmsIdNotation | undefined;
|
||||
smsIdFormat?: SmsIdFormat | undefined;
|
||||
session: Session;
|
||||
systemId?: string | undefined;
|
||||
};
|
||||
@@ -30,8 +30,8 @@ export class IncomingRequests {
|
||||
private readonly log: SmppLog;
|
||||
private readonly onRequest: OnRequest | undefined;
|
||||
private readonly reassembler: Reassembler;
|
||||
private readonly receiptIdNotation: SmsIdNotation | undefined;
|
||||
private readonly session: Session;
|
||||
private readonly smsIdFormat: SmsIdFormat;
|
||||
private readonly systemId: string;
|
||||
|
||||
constructor(options: IncomingRequestsOptions) {
|
||||
@@ -44,8 +44,8 @@ export class IncomingRequests {
|
||||
maxOctets: options.maxOctets,
|
||||
timeout: options.reassemblyTimeout ?? defaults.reassemblyTimeout,
|
||||
});
|
||||
this.receiptIdNotation = options.receiptIdNotation;
|
||||
this.session = options.session;
|
||||
this.smsIdFormat = options.smsIdFormat ?? {};
|
||||
this.systemId = options.systemId ?? defaults.systemId;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ export class IncomingRequests {
|
||||
|
||||
/** SMPP carries a mobile-originated message and a delivery receipt on the same command. */
|
||||
private async onDeliverSm(pduObj: PduObject): Promise<void> {
|
||||
const dlr = dlrFromPdu(pduObj, this.receiptIdNotation);
|
||||
const dlr = dlrFromPdu(pduObj, this.smsIdFormat);
|
||||
|
||||
if (!dlr) {
|
||||
this.onMessage(pduObj);
|
||||
|
||||
@@ -145,17 +145,17 @@ function checkSmsIdFormat(smsIdFormat: unknown): VoidResult {
|
||||
return { err: new Error('smsIdFormat names a notation per place, as { receipt, submitResp }') };
|
||||
}
|
||||
|
||||
const allowed = smsIdNotations.join(' or ');
|
||||
|
||||
for (const place of smsIdPlaces) {
|
||||
const notation = smsIdFormat[place];
|
||||
for (const [place, notation] of Object.entries(smsIdFormat)) {
|
||||
if (!smsIdPlaces.includes(place)) {
|
||||
return { err: new Error(`smsIdFormat has no ${place}, name ${smsIdPlaces.join(' or ')}`) };
|
||||
}
|
||||
|
||||
if (notation === undefined || isSmsIdNotation(notation)) continue;
|
||||
|
||||
// String() throws on a null-prototype object, and this value is whatever the caller passed.
|
||||
const got = typeof notation === 'string' ? notation : typeof notation;
|
||||
|
||||
return { err: new Error(`smsIdFormat.${place} must be ${allowed}, got ${got}`) };
|
||||
return { err: new Error(`smsIdFormat.${place} must be ${smsIdNotations.join(' or ')}, got ${got}`) };
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
+1
-1
@@ -120,8 +120,8 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
maxReassembly: options.maxReassembly,
|
||||
onRequest: options.onRequest,
|
||||
reassemblyTimeout: options.reassemblyTimeout,
|
||||
receiptIdNotation: options.smsIdFormat?.receipt,
|
||||
session: this,
|
||||
smsIdFormat: options.smsIdFormat,
|
||||
systemId: options.systemId,
|
||||
});
|
||||
this.pending = new PendingRequests(this.log);
|
||||
|
||||
+6
-8
@@ -3,18 +3,17 @@ const notations = {
|
||||
hex: { digits: /^[0-9a-f]+$/i, prefix: '0x' },
|
||||
};
|
||||
|
||||
const places = ['receipt', 'submitResp'] as const;
|
||||
|
||||
/** The notation a peer writes message ids in. */
|
||||
export type SmsIdNotation = keyof typeof notations;
|
||||
|
||||
/** The notation per place the peer writes an id. An omitted place is left as it arrived. */
|
||||
export type SmsIdFormat = {
|
||||
receipt?: SmsIdNotation | undefined;
|
||||
submitResp?: SmsIdNotation | undefined;
|
||||
};
|
||||
export type SmsIdFormat = Partial<Record<typeof places[number], SmsIdNotation | undefined>>;
|
||||
|
||||
export const smsIdNotations: string[] = Object.keys(notations);
|
||||
|
||||
export const smsIdPlaces: (keyof SmsIdFormat)[] = ['receipt', 'submitResp'];
|
||||
export const smsIdPlaces: readonly string[] = places;
|
||||
|
||||
export function isSmsIdNotation(value: unknown): value is SmsIdNotation {
|
||||
return typeof value === 'string' && Object.hasOwn(notations, value);
|
||||
@@ -25,11 +24,10 @@ const maxIdLength = 64;
|
||||
|
||||
/**
|
||||
* The id as a plain decimal value, so an SMSC that answers a submit in one notation and writes the
|
||||
* receipt in another still correlates. An id the notation cannot read is left as it arrived, which
|
||||
* is what leaves a `<base>-<n>` id whole for DlrMerger.
|
||||
* receipt in another still correlates.
|
||||
*/
|
||||
export function normaliseSmsId(id: string, notation: SmsIdNotation | undefined): string {
|
||||
if (notation === undefined || id.length > maxIdLength) return id;
|
||||
if (!isSmsIdNotation(notation) || id.length > maxIdLength) return id;
|
||||
|
||||
const { digits, prefix } = notations[notation];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user