-
Notifications
You must be signed in to change notification settings - Fork 10
/
delivery_sm.go
238 lines (217 loc) · 6.15 KB
/
delivery_sm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package ucp
import (
"bufio"
"encoding/hex"
"fmt"
"sort"
"strconv"
"sync"
"github.com/go-gsm/charset"
)
type deliverShortMessage struct {
AdC []byte
OAdC []byte
AC []byte
NRq []byte
NAdC []byte
NT []byte
NPID []byte
LRq []byte
LRAd []byte
LPID []byte
DD []byte
DDT []byte
VP []byte
RPID []byte
SCTS []byte
Dst []byte
Rsn []byte
DSCTS []byte
MT []byte
NB []byte
Msg []byte
MMS []byte
PR []byte
DCs []byte
MCLs []byte
RPI []byte
CPg []byte
RPLy []byte
OTOA []byte
HPLMN []byte
Xser []byte
RES4 []byte
RES5 []byte
}
// deliverySmAck represents a data structure of an acknowledgment packet for deliver sm.
type deliverySmAck struct {
Ack []byte
ModifiedValidityPeriod []byte
SystemMessage []byte
}
func (s deliverySmAck) Code() []byte {
return []byte(opDeliveryShortMessage)
}
func (s deliverySmAck) Type() []byte {
return []byte(resultType)
}
// deliveryAckPDU builds a deliveryNotifAck packet
func deliverySmAckPacket(refNum []byte, systemMessage string) []byte {
ack := deliverySmAck{
Ack: []byte(positiveAck),
SystemMessage: []byte(systemMessage),
}
buf := preparePacket(refNum, ack)
return buf
}
// readDeliveryMsg reads all deliver sm messages(mobile-originating messages) from the deliverMsgCh channel.
func readDeliveryMsg(writer *bufio.Writer, wg *sync.WaitGroup, closeChan chan struct{},
deliverMsgCh chan []string, deliverMsgPartCh, deliverMsgCompleteCh chan deliverMsgPart, mu *sync.Mutex, logger Logger) {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-closeChan:
logger.Printf("readDeliveryMsg terminated\n")
return
case mo := <-deliverMsgCh:
xser := mo[xserIndex]
xserData := parseXser(xser)
msg := mo[moMsgIndex]
refNum := mo[refNumIndex]
sender := mo[moSenderIndex]
recvr := mo[moRecvrIndex]
scts := mo[moSctsIndex]
sysmsg := recvr + ":" + scts
msgID := sender + ":" + scts
mu.Lock()
// send ack to SMSC with the same reference number
if _, err := writer.Write(deliverySmAckPacket([]byte(refNum), sysmsg)); err != nil {
logger.Printf("error writing delivery sm ack packet: %v\n", err)
}
if err := writer.Flush(); err != nil {
logger.Printf("error flushing delivery sm ack packet: %v\n", err)
}
mu.Unlock()
var incomingMsg deliverMsgPart
incomingMsg.sender = sender
incomingMsg.receiver = recvr
incomingMsg.message = msg
incomingMsg.msgID = msgID
if xserDCS, ok := xserData[dcsXserKey]; ok {
incomingMsg.dcs = xserDCS
}
// check the user data header extra service field
// if it exists, the incoming message has multiple parts
if xserUdh, ok := xserData[udhXserKey]; ok {
// handle multipart mobile originating message i.e. len(message) > 140 bytes
// get the total message parts in the xser data
msgPartsLen := xserUdh[len(xserUdh)-4 : len(xserUdh)-2]
// get the current message part in the xser data
msgPart := xserUdh[len(xserUdh)-2:]
// get UDH reference number
msgRefNum := xserUdh[len(xserUdh)-6 : len(xserUdh)-4]
// convert hexstring to integer
msgRefNumInt, _ := strconv.ParseInt(msgRefNum, 16, 0)
msgPartsLenInt, _ := strconv.ParseInt(msgPartsLen, 16, 64)
msgPartInt, _ := strconv.ParseInt(msgPart, 16, 64)
// convert int64 to int
incomingMsg.currentPart = int(msgPartInt)
incomingMsg.totalParts = int(msgPartsLenInt)
incomingMsg.refNum = int(msgRefNumInt)
// send to partial channel
deliverMsgPartCh <- incomingMsg
} else {
// handle mobile originating message with only 1 part i.e. len(message) <= 140 bytes
// send the incoming message to the complete channel
deliverMsgCompleteCh <- incomingMsg
}
}
}
}()
}
// readPartialDeliveryMsg concatenates partial incoming mobile-originating messages
func readPartialDeliveryMsg(wg *sync.WaitGroup, closeChan chan struct{},
deliverMsgPartCh, deliverMsgCompleteCh chan deliverMsgPart, logger Logger) {
wg.Add(1)
go func() {
defer wg.Done()
concatMap := make(map[string][]deliverMsgPart)
for {
select {
case <-closeChan:
logger.Printf("readPartialDeliveryMsg terminated\n")
return
case partial := <-deliverMsgPartCh:
mapKey := fmt.Sprintf("%s:%s:%d", partial.sender, partial.receiver, partial.refNum)
partMsgList, ok := concatMap[mapKey]
if ok {
partMsgList = append(partMsgList, partial)
concatMap[mapKey] = partMsgList
if len(partMsgList) == partial.totalParts {
sort.Slice(partMsgList, func(i, j int) bool {
return partMsgList[i].currentPart < partMsgList[j].currentPart
})
var fullMsg string
for _, partMsg := range partMsgList {
fullMsg += partMsg.message
}
partial.message = fullMsg
deliverMsgCompleteCh <- partial
delete(concatMap, mapKey)
}
} else {
concatMap[mapKey] = []deliverMsgPart{partial}
}
}
}
}()
}
// readPartialDeliveryMsg processes complete incoming mobile-originating messages
func readCompleteDeliveryMsg(wg *sync.WaitGroup, closeChan chan struct{},
deliverMsgCompleteCh chan deliverMsgPart, shortMessageHandler Handler, accessCode string, logger Logger) {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-closeChan:
logger.Printf("readCompleteDeliveryMsg terminated\n")
return
case complete := <-deliverMsgCompleteCh:
shortMessageHandler(
complete.sender,
complete.receiver,
complete.msgID,
encodeDeliverMsg(complete.message, complete.dcs),
accessCode,
)
}
}
}()
}
// encodeDeliverMsg encodes a mobile-originating message
func encodeDeliverMsg(mo string, dcs string) string {
var msg string
if dcs == dcsXserASCII {
msgByte, _ := charset.ParseOddHexStr(mo)
msg, _ = charset.Decode7Bit(msgByte)
}
if dcs == dcsXserUCS2 {
decoded, _ := hex.DecodeString(mo)
msg, _ = charset.DecodeUcs2(decoded)
}
return msg
}
// deliverMsgPart represents a deliver sm message part
type deliverMsgPart struct {
currentPart int
totalParts int
refNum int
sender string
receiver string
message string
msgID string
dcs string
}