v4_handler_test.go 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. package icq_legacy
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "log/slog"
  8. "net"
  9. "testing"
  10. "time"
  11. "github.com/mk6i/open-oscar-server/config"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/mock"
  14. "github.com/stretchr/testify/require"
  15. )
  16. const (
  17. testV4UIN = uint32(12345)
  18. testV4Seq = uint16(42)
  19. testV4Seq2 = uint16(7)
  20. )
  21. var testV4Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4000}
  22. type v4HandleEnv struct {
  23. T *testing.T
  24. Sender *mockPacketSender
  25. Service *mockLegacyService
  26. Sessions *LegacySessionManager
  27. Session *LegacySession
  28. }
  29. type v4HandleCase struct {
  30. name string
  31. session *LegacySession
  32. pkt []byte
  33. wire func(t *testing.T) []byte
  34. setup func(v4HandleEnv) func(error)
  35. }
  36. func v4CaseSetup(pre func(v4HandleEnv), after func(e v4HandleEnv, err error)) func(v4HandleEnv) func(error) {
  37. return func(e v4HandleEnv) func(error) {
  38. if pre != nil {
  39. pre(e)
  40. }
  41. return func(err error) {
  42. if after != nil {
  43. after(e, err)
  44. return
  45. }
  46. assert.NoError(e.T, err)
  47. }
  48. }
  49. }
  50. func runV4HandleCases(t *testing.T, cases []v4HandleCase) {
  51. t.Helper()
  52. for _, tt := range cases {
  53. t.Run(tt.name, func(t *testing.T) {
  54. sender := newMockPacketSender(t)
  55. svc := newMockLegacyService(t)
  56. sessions := newTestLegacySessionManager()
  57. e := v4HandleEnv{
  58. T: t,
  59. Sender: sender,
  60. Service: svc,
  61. Sessions: sessions,
  62. Session: tt.session,
  63. }
  64. var check func(error)
  65. if tt.setup != nil {
  66. check = tt.setup(e)
  67. } else {
  68. check = func(err error) { assert.NoError(t, err) }
  69. }
  70. h := newTestV4Handler(t, sender, svc, sessions)
  71. var wire []byte
  72. switch {
  73. case tt.wire != nil:
  74. wire = tt.wire(t)
  75. case tt.pkt != nil:
  76. wire = tt.pkt
  77. default:
  78. t.Fatal("case must set pkt or wire")
  79. }
  80. check(h.Handle(tt.session, testV4Addr, wire))
  81. })
  82. }
  83. }
  84. func newTestV4Handler(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V4Handler {
  85. t.Helper()
  86. if sessions == nil {
  87. sessions = newTestLegacySessionManager()
  88. }
  89. return NewV4Handler(sessions, svc, sender, NewV4PacketBuilder(sessions, nil), slog.Default())
  90. }
  91. func v4Session(uin uint32) *LegacySession {
  92. s := newTestLegacySession(uin, legacySessionOptOSCARSess, func(ls *LegacySession) {
  93. ls.Version = ICQLegacyVersionV4
  94. ls.SeqNumClient = testV4Seq
  95. })
  96. return s
  97. }
  98. // v4SrvCommand reads the command from outbound V3-format server packets (V4 wire uses
  99. // ICQLegacyVersionV3 headers with checkcode for server→client replies).
  100. func v4SrvCommand(packet []byte) uint16 {
  101. return v3ServerCommand(packet)
  102. }
  103. func expectV4NotConnected(sender *mockPacketSender) {
  104. sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  105. Run(func(_ *net.UDPAddr, p []byte) {
  106. if got := v3ServerCommand(p); got != ICQLegacySrvNotConnected {
  107. panic(fmt.Sprintf("got command 0x%04X, want NOT_CONNECTED", got))
  108. }
  109. }).Return(nil).Once()
  110. }
  111. func transformV4Packet(packet []byte) error {
  112. if len(packet) < v4HeaderSize {
  113. return fmt.Errorf("packet too short for V4 transform")
  114. }
  115. checkcode := binary.LittleEndian.Uint32(packet[v4OffsetCheckcode:])
  116. packetLen := len(packet)
  117. key := uint32(packetLen)*0x66756B65 + checkcode
  118. count := ((packetLen+3)/4 + 3) / 4
  119. for i := 0; i < count; i++ {
  120. pos := i * 4
  121. if i == 4 {
  122. continue
  123. }
  124. if pos >= packetLen {
  125. break
  126. }
  127. tableIdx := pos & 0xFF
  128. xorVal := key + uint32(V4Table[tableIdx])
  129. for j := 0; j < 4 && pos+j < packetLen; j++ {
  130. packet[pos+j] ^= byte(xorVal >> (j * 8))
  131. }
  132. }
  133. packet[0] = 0x04
  134. packet[1] = 0x00
  135. return nil
  136. }
  137. func buildV4ClientPacket(t *testing.T, command, seq1, seq2 uint16, uin uint32, data []byte) []byte {
  138. t.Helper()
  139. buf := make([]byte, v4HeaderSize+len(data))
  140. binary.LittleEndian.PutUint16(buf[v4OffsetVersion:], ICQLegacyVersionV4)
  141. binary.LittleEndian.PutUint16(buf[v4OffsetRandom:], 0x1234)
  142. binary.LittleEndian.PutUint16(buf[v4OffsetZero:], 0)
  143. binary.LittleEndian.PutUint16(buf[v4OffsetCommand:], command)
  144. binary.LittleEndian.PutUint16(buf[v4OffsetSeq1:], seq1)
  145. binary.LittleEndian.PutUint16(buf[v4OffsetSeq2:], seq2)
  146. binary.LittleEndian.PutUint32(buf[v4OffsetUIN:], uin)
  147. binary.LittleEndian.PutUint32(buf[v4OffsetCheckcode:], 0)
  148. copy(buf[v4HeaderSize:], data)
  149. require.NoError(t, transformV4Packet(buf))
  150. verify := make([]byte, len(buf))
  151. copy(verify, buf)
  152. require.NoError(t, transformV4Packet(verify))
  153. require.Equal(t, command, binary.LittleEndian.Uint16(verify[v4OffsetCommand:]))
  154. require.Equal(t, uin, binary.LittleEndian.Uint32(verify[v4OffsetUIN:]))
  155. return buf
  156. }
  157. func defaultV4Pkt(command uint16, data []byte) func(t *testing.T) []byte {
  158. return func(t *testing.T) []byte {
  159. return buildV4ClientPacket(t, command, testV4Seq, testV4Seq2, testV4UIN, data)
  160. }
  161. }
  162. func v4GetDepsPayload(uin uint32, password string) []byte {
  163. pwd := []byte(password)
  164. buf := make([]byte, 4+2+len(pwd))
  165. binary.LittleEndian.PutUint32(buf[0:4], uin)
  166. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
  167. copy(buf[6:], pwd)
  168. return buf
  169. }
  170. func v4LoginData(password string) []byte {
  171. pwd := []byte(password)
  172. buf := make([]byte, 4+4+2+len(pwd))
  173. binary.LittleEndian.PutUint32(buf[0:4], 0)
  174. binary.LittleEndian.PutUint32(buf[4:8], 4000)
  175. binary.LittleEndian.PutUint16(buf[8:10], uint16(len(pwd)))
  176. copy(buf[10:], pwd)
  177. return buf
  178. }
  179. func v4ContactListData(uins []uint32) []byte {
  180. buf := make([]byte, 1+4*len(uins))
  181. buf[0] = byte(len(uins))
  182. for i, u := range uins {
  183. binary.LittleEndian.PutUint32(buf[1+4*i:], u)
  184. }
  185. return buf
  186. }
  187. func v4MessageData(toUIN uint32, msgType uint16, message string) []byte {
  188. msg := []byte(message)
  189. buf := make([]byte, 4+2+2+len(msg))
  190. binary.LittleEndian.PutUint32(buf[0:4], toUIN)
  191. binary.LittleEndian.PutUint16(buf[4:6], msgType)
  192. binary.LittleEndian.PutUint16(buf[6:8], uint16(len(msg)))
  193. copy(buf[8:], msg)
  194. return buf
  195. }
  196. func v4TargetUINData(target uint32) []byte {
  197. buf := make([]byte, 4)
  198. binary.LittleEndian.PutUint32(buf, target)
  199. return buf
  200. }
  201. func v4WriteBufLE(buf *bytes.Buffer, v any) {
  202. _ = binary.Write(buf, binary.LittleEndian, v)
  203. }
  204. func TestBuildV4ClientPacket_roundTrip(t *testing.T) {
  205. wire := buildV4ClientPacket(t, ICQLegacyCmdKeepAlive, testV4Seq, testV4Seq2, testV4UIN, nil)
  206. plain := make([]byte, len(wire))
  207. copy(plain, wire)
  208. require.NoError(t, transformV4Packet(plain))
  209. require.Equal(t, ICQLegacyCmdKeepAlive, binary.LittleEndian.Uint16(plain[v4OffsetCommand:]))
  210. require.Equal(t, testV4UIN, binary.LittleEndian.Uint32(plain[v4OffsetUIN:]))
  211. }
  212. func v4SrvData(packet []byte) []byte {
  213. if len(packet) <= 16 {
  214. return nil
  215. }
  216. return packet[16:]
  217. }
  218. func v4UpdateBasicData() []byte {
  219. buf := new(bytes.Buffer)
  220. writeLegacyString(buf, "nick")
  221. writeLegacyString(buf, "first")
  222. writeLegacyString(buf, "last")
  223. writeLegacyString(buf, "a@b.com")
  224. buf.WriteByte(0)
  225. return buf.Bytes()
  226. }
  227. func v4UpdateDetailData() []byte {
  228. buf := new(bytes.Buffer)
  229. writeLegacyString(buf, "city")
  230. v4WriteBufLE(buf, uint16(840))
  231. buf.WriteByte(0)
  232. writeLegacyString(buf, "CA")
  233. v4WriteBufLE(buf, uint16(25))
  234. buf.WriteByte(1)
  235. writeLegacyString(buf, "555")
  236. writeLegacyString(buf, "http://example.com")
  237. writeLegacyString(buf, "about me")
  238. v4WriteBufLE(buf, uint32(0))
  239. return buf.Bytes()
  240. }
  241. func v4RegNewUserData(nick, first, last, email string) []byte {
  242. buf := new(bytes.Buffer)
  243. v4WriteBufLE(buf, uint16(0))
  244. writeLegacyString(buf, nick)
  245. writeLegacyString(buf, first)
  246. writeLegacyString(buf, last)
  247. writeLegacyString(buf, email)
  248. return buf.Bytes()
  249. }
  250. func TestV4Handler_Handle(t *testing.T) {
  251. runV4HandleCases(t, []v4HandleCase{
  252. {
  253. name: "packet too short",
  254. pkt: []byte{0x01, 0x02},
  255. setup: v4CaseSetup(nil, func(e v4HandleEnv, err error) {
  256. require.Error(e.T, err)
  257. assert.Contains(e.T, err.Error(), "V4 packet too short")
  258. }),
  259. },
  260. {
  261. name: "header too short for decrypt",
  262. pkt: make([]byte, v4HeaderSize-1),
  263. setup: v4CaseSetup(nil, func(e v4HandleEnv, err error) {
  264. require.Error(e.T, err)
  265. assert.Contains(e.T, err.Error(), "too short")
  266. }),
  267. },
  268. {
  269. name: "nil session disallowed command",
  270. session: nil,
  271. wire: defaultV4Pkt(ICQLegacyCmdKeepAlive, nil),
  272. setup: v4CaseSetup(func(e v4HandleEnv) {
  273. expectV4NotConnected(e.Sender)
  274. }, nil),
  275. },
  276. {
  277. name: "nil session ACK allowed",
  278. session: nil,
  279. wire: defaultV4Pkt(ICQLegacyCmdAck, nil),
  280. },
  281. {
  282. name: "session state updated",
  283. session: v4Session(testV4UIN),
  284. wire: defaultV4Pkt(ICQLegacyCmdAck, nil),
  285. setup: v4CaseSetup(nil, func(e v4HandleEnv, err error) {
  286. assert.NoError(e.T, err)
  287. assert.Equal(e.T, testV4Seq, e.Session.SeqNumClient)
  288. assert.WithinDuration(e.T, time.Now(), e.Session.GetLastActivity(), time.Second)
  289. }),
  290. },
  291. {
  292. name: "unknown command with session",
  293. session: v4Session(testV4UIN),
  294. wire: defaultV4Pkt(0xFFFF, nil),
  295. setup: v4CaseSetup(func(e v4HandleEnv) {
  296. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  297. Run(func(_ *net.UDPAddr, p []byte) {
  298. assert.Equal(e.T, ICQLegacySrvAck, v4SrvCommand(p))
  299. }).Return(nil).Once()
  300. }, nil),
  301. },
  302. {
  303. name: "SysMsgDoneAck no-op",
  304. session: v4Session(testV4UIN),
  305. wire: defaultV4Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
  306. },
  307. })
  308. }
  309. func TestV4Handler_HandleFirstLogin(t *testing.T) {
  310. runV4HandleCases(t, []v4HandleCase{
  311. {
  312. name: "first login",
  313. session: nil,
  314. wire: defaultV4Pkt(ICQLegacyCmdFirstLogin, nil),
  315. setup: v4CaseSetup(func(e v4HandleEnv) {
  316. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  317. Run(func(_ *net.UDPAddr, p []byte) {
  318. assert.Equal(e.T, ICQLegacySrvAck, v4SrvCommand(p))
  319. }).Return(nil).Once()
  320. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  321. Run(func(_ *net.UDPAddr, p []byte) {
  322. assert.Equal(e.T, ICQLegacySrvRegisterInfo, v4SrvCommand(p))
  323. }).Return(nil).Once()
  324. }, nil),
  325. },
  326. })
  327. }
  328. func TestV4Handler_HandleRegRequestInfo(t *testing.T) {
  329. runV4HandleCases(t, []v4HandleCase{
  330. {
  331. name: "registration info request",
  332. session: nil,
  333. wire: defaultV4Pkt(ICQLegacyCmdRegRequestInfo, nil),
  334. setup: v4CaseSetup(func(e v4HandleEnv) {
  335. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  336. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  337. Run(func(_ *net.UDPAddr, p []byte) {
  338. assert.Equal(e.T, ICQLegacySrvRegisterInfo, v4SrvCommand(p))
  339. }).Return(nil).Once()
  340. }, nil),
  341. },
  342. })
  343. }
  344. func TestV4Handler_HandleRegNewUserInfo(t *testing.T) {
  345. runV4HandleCases(t, []v4HandleCase{
  346. {
  347. name: "success",
  348. session: nil,
  349. wire: defaultV4Pkt(ICQLegacyCmdRegNewUserInfo, v4RegNewUserData("nick", "first", "last", "a@b.com")),
  350. setup: v4CaseSetup(func(e v4HandleEnv) {
  351. e.Service.EXPECT().RegisterNewUser(mock.Anything, "nick", "first", "last", "a@b.com", mock.AnythingOfType("string")).
  352. Return(uint32(100001), nil).Once()
  353. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  354. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  355. Run(func(_ *net.UDPAddr, p []byte) {
  356. assert.Equal(e.T, ICQLegacySrvNewUIN, v4SrvCommand(p))
  357. assert.Equal(e.T, uint32(100001), binary.LittleEndian.Uint32(p[8:12]))
  358. }).Return(nil).Once()
  359. }, nil),
  360. },
  361. {
  362. name: "data too short",
  363. session: nil,
  364. wire: defaultV4Pkt(ICQLegacyCmdRegNewUserInfo, []byte{0, 0}),
  365. setup: v4CaseSetup(func(e v4HandleEnv) {
  366. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  367. expectV4NotConnected(e.Sender)
  368. }, nil),
  369. },
  370. })
  371. }
  372. func TestV4Handler_HandleGetDeps(t *testing.T) {
  373. runV4HandleCases(t, []v4HandleCase{
  374. {
  375. name: "valid credentials",
  376. session: nil,
  377. wire: defaultV4Pkt(ICQLegacyCmdGetDeps, v4GetDepsPayload(testV4UIN, "secret")),
  378. setup: v4CaseSetup(func(e v4HandleEnv) {
  379. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV4UIN, "secret").Return(true, nil).Once()
  380. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  381. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
  382. Run(func(_ *LegacySession, p []byte) {
  383. assert.Equal(e.T, ICQLegacySrvUserDepsList, v4SrvCommand(p))
  384. }).Return(nil).Once()
  385. }, func(e v4HandleEnv, err error) {
  386. assert.NoError(e.T, err)
  387. assert.NotNil(e.T, e.Sessions.GetSession(testV4UIN))
  388. }),
  389. },
  390. {
  391. name: "invalid credentials",
  392. session: nil,
  393. wire: defaultV4Pkt(ICQLegacyCmdGetDeps, v4GetDepsPayload(testV4UIN, "wrong")),
  394. setup: v4CaseSetup(func(e v4HandleEnv) {
  395. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV4UIN, "wrong").Return(false, nil).Once()
  396. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  397. Run(func(_ *net.UDPAddr, p []byte) {
  398. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v4SrvCommand(p))
  399. }).Return(nil).Once()
  400. }, nil),
  401. },
  402. {
  403. name: "service error",
  404. session: nil,
  405. wire: defaultV4Pkt(ICQLegacyCmdGetDeps, v4GetDepsPayload(testV4UIN, "secret")),
  406. setup: v4CaseSetup(func(e v4HandleEnv) {
  407. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV4UIN, "secret").
  408. Return(false, errors.New("db down")).Once()
  409. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  410. Run(func(_ *net.UDPAddr, p []byte) {
  411. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v4SrvCommand(p))
  412. }).Return(nil).Once()
  413. }, nil),
  414. },
  415. {
  416. name: "packet too short",
  417. session: nil,
  418. wire: defaultV4Pkt(ICQLegacyCmdGetDeps, []byte{0x01}),
  419. setup: v4CaseSetup(nil, func(e v4HandleEnv, err error) {
  420. assert.NoError(e.T, err)
  421. }),
  422. },
  423. })
  424. }
  425. func TestV4Handler_HandleLogin(t *testing.T) {
  426. runV4HandleCases(t, []v4HandleCase{
  427. {
  428. name: "auth success direct login",
  429. session: nil,
  430. wire: defaultV4Pkt(ICQLegacyCmdLogin, v4LoginData("secret")),
  431. setup: v4CaseSetup(func(e v4HandleEnv) {
  432. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
  433. return req.UIN == testV4UIN && req.Password == "secret" && req.Version == ICQLegacyVersionV4
  434. })).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
  435. e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
  436. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  437. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
  438. }, func(e v4HandleEnv, err error) {
  439. assert.NoError(e.T, err)
  440. assert.NotNil(e.T, e.Sessions.GetSession(testV4UIN))
  441. }),
  442. },
  443. {
  444. name: "bad password",
  445. session: nil,
  446. wire: defaultV4Pkt(ICQLegacyCmdLogin, v4LoginData("wrong")),
  447. setup: v4CaseSetup(func(e v4HandleEnv) {
  448. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
  449. Return(&AuthResult{Success: false, ErrorCode: 0x0001}, nil).Once()
  450. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  451. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  452. Run(func(_ *net.UDPAddr, p []byte) {
  453. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v4SrvCommand(p))
  454. }).Return(nil).Once()
  455. }, nil),
  456. },
  457. {
  458. name: "parse error",
  459. session: nil,
  460. wire: defaultV4Pkt(ICQLegacyCmdLogin, []byte{0, 0, 0}),
  461. setup: v4CaseSetup(func(e v4HandleEnv) {
  462. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  463. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  464. Run(func(_ *net.UDPAddr, p []byte) {
  465. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v4SrvCommand(p))
  466. }).Return(nil).Once()
  467. }, nil),
  468. },
  469. })
  470. }
  471. func TestV4Handler_HandleContactList(t *testing.T) {
  472. runV4HandleCases(t, []v4HandleCase{
  473. {
  474. name: "valid contact list",
  475. session: v4Session(testV4UIN),
  476. wire: defaultV4Pkt(ICQLegacyCmdContactList, v4ContactListData([]uint32{99999})),
  477. setup: v4CaseSetup(func(e v4HandleEnv) {
  478. e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
  479. Return(&ContactListResult{}, nil).Once()
  480. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  481. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  482. Run(func(_ *LegacySession, p []byte) {
  483. assert.Equal(e.T, ICQLegacySrvUserListDone, v4SrvCommand(p))
  484. }).Return(nil).Once()
  485. }, nil),
  486. },
  487. {
  488. name: "nil session",
  489. wire: defaultV4Pkt(ICQLegacyCmdContactList, v4ContactListData(nil)),
  490. setup: v4CaseSetup(func(e v4HandleEnv) {
  491. expectV4NotConnected(e.Sender)
  492. }, nil),
  493. },
  494. })
  495. }
  496. func TestV4Handler_HandlePing(t *testing.T) {
  497. runV4HandleCases(t, []v4HandleCase{
  498. {
  499. name: "keep alive",
  500. session: v4Session(testV4UIN),
  501. wire: defaultV4Pkt(ICQLegacyCmdKeepAlive, nil),
  502. setup: v4CaseSetup(func(e v4HandleEnv) {
  503. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  504. Run(func(_ *net.UDPAddr, p []byte) {
  505. assert.Equal(e.T, ICQLegacySrvAck, v4SrvCommand(p))
  506. }).Return(nil).Once()
  507. }, nil),
  508. },
  509. {
  510. name: "keep alive2",
  511. session: v4Session(testV4UIN),
  512. wire: defaultV4Pkt(ICQLegacyCmdKeepAlive2, nil),
  513. setup: v4CaseSetup(func(e v4HandleEnv) {
  514. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  515. }, nil),
  516. },
  517. {
  518. name: "nil session",
  519. wire: defaultV4Pkt(ICQLegacyCmdKeepAlive, nil),
  520. setup: v4CaseSetup(func(e v4HandleEnv) {
  521. expectV4NotConnected(e.Sender)
  522. }, nil),
  523. },
  524. })
  525. }
  526. func TestV4Handler_HandleLogoff(t *testing.T) {
  527. runV4HandleCases(t, []v4HandleCase{
  528. {
  529. name: "active session",
  530. session: v4Session(testV4UIN),
  531. wire: defaultV4Pkt(ICQLegacyCmdLogoff, nil),
  532. setup: v4CaseSetup(func(e v4HandleEnv) {
  533. registerSession(e.Sessions, e.Session)
  534. e.Service.EXPECT().NotifyUserOffline(mock.Anything, testV4UIN).Return(nil).Once()
  535. }, func(e v4HandleEnv, err error) {
  536. assert.NoError(e.T, err)
  537. assert.Nil(e.T, e.Sessions.GetSession(testV4UIN))
  538. }),
  539. },
  540. {
  541. name: "nil session",
  542. wire: defaultV4Pkt(ICQLegacyCmdLogoff, nil),
  543. setup: v4CaseSetup(func(e v4HandleEnv) {
  544. expectV4NotConnected(e.Sender)
  545. }, nil),
  546. },
  547. })
  548. }
  549. func TestV4Handler_HandleSetStatus(t *testing.T) {
  550. runV4HandleCases(t, []v4HandleCase{
  551. {
  552. name: "valid status",
  553. session: v4Session(testV4UIN),
  554. wire: defaultV4Pkt(ICQLegacyCmdSetStatus, func() []byte {
  555. b := make([]byte, 4)
  556. binary.LittleEndian.PutUint32(b, 0x00000001)
  557. return b
  558. }()),
  559. setup: v4CaseSetup(func(e v4HandleEnv) {
  560. e.Service.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  561. Return(&StatusChangeResult{}, nil).Once()
  562. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  563. }, func(e v4HandleEnv, err error) {
  564. assert.NoError(e.T, err)
  565. assert.Equal(e.T, uint32(0x00000001), e.Session.GetStatus())
  566. }),
  567. },
  568. {
  569. name: "nil session",
  570. wire: defaultV4Pkt(ICQLegacyCmdSetStatus, func() []byte {
  571. b := make([]byte, 4)
  572. binary.LittleEndian.PutUint32(b, 1)
  573. return b
  574. }()),
  575. setup: v4CaseSetup(func(e v4HandleEnv) {
  576. expectV4NotConnected(e.Sender)
  577. }, nil),
  578. },
  579. })
  580. }
  581. func TestV4Handler_HandleMessage(t *testing.T) {
  582. target := v4Session(99999)
  583. runV4HandleCases(t, []v4HandleCase{
  584. {
  585. name: "offline stored",
  586. session: v4Session(testV4UIN),
  587. wire: defaultV4Pkt(ICQLegacyCmdThruServer, v4MessageData(99999, 0x0001, "hi")),
  588. setup: v4CaseSetup(func(e v4HandleEnv) {
  589. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  590. Return(&MessageResult{StoredOffline: true}, nil).Once()
  591. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  592. }, nil),
  593. },
  594. {
  595. name: "online target",
  596. session: v4Session(testV4UIN),
  597. wire: defaultV4Pkt(ICQLegacyCmdThruServer, v4MessageData(99999, 0x0001, "hi")),
  598. setup: v4CaseSetup(func(e v4HandleEnv) {
  599. registerSession(e.Sessions, target)
  600. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  601. Return(&MessageResult{TargetOnline: true}, nil).Once()
  602. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  603. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
  604. }, nil),
  605. },
  606. {
  607. name: "nil session",
  608. wire: defaultV4Pkt(ICQLegacyCmdThruServer, v4MessageData(99999, 0x0001, "hi")),
  609. setup: v4CaseSetup(func(e v4HandleEnv) {
  610. expectV4NotConnected(e.Sender)
  611. }, nil),
  612. },
  613. })
  614. }
  615. func TestV4Handler_HandleAuthorize(t *testing.T) {
  616. runV4HandleCases(t, []v4HandleCase{
  617. {
  618. name: "authorize message",
  619. session: v4Session(testV4UIN),
  620. wire: defaultV4Pkt(ICQLegacyCmdAuthorize, v4MessageData(99999, 0x0001, "auth")),
  621. setup: v4CaseSetup(func(e v4HandleEnv) {
  622. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  623. Return(&MessageResult{StoredOffline: true}, nil).Once()
  624. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  625. }, nil),
  626. },
  627. })
  628. }
  629. func TestV4Handler_HandleUserAdd(t *testing.T) {
  630. runV4HandleCases(t, []v4HandleCase{
  631. {
  632. name: "success",
  633. session: v4Session(testV4UIN),
  634. wire: defaultV4Pkt(ICQLegacyCmdUserAdd, v4TargetUINData(99999)),
  635. setup: v4CaseSetup(func(e v4HandleEnv) {
  636. e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  637. Return(&UserAddResult{}, nil).Once()
  638. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  639. }, func(e v4HandleEnv, err error) {
  640. assert.Contains(e.T, e.Session.GetContactList(), uint32(99999))
  641. }),
  642. },
  643. {
  644. name: "nil session",
  645. wire: defaultV4Pkt(ICQLegacyCmdUserAdd, v4TargetUINData(99999)),
  646. setup: v4CaseSetup(func(e v4HandleEnv) {
  647. expectV4NotConnected(e.Sender)
  648. }, nil),
  649. },
  650. })
  651. }
  652. func TestV4Handler_HandleOfflineMsgReq(t *testing.T) {
  653. runV4HandleCases(t, []v4HandleCase{
  654. {
  655. name: "no messages",
  656. session: v4Session(testV4UIN),
  657. wire: defaultV4Pkt(ICQLegacyCmdSysMsgReq, nil),
  658. setup: v4CaseSetup(func(e v4HandleEnv) {
  659. e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV4UIN).
  660. Return([]LegacyOfflineMessage{}, nil).Once()
  661. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  662. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  663. Run(func(_ *LegacySession, p []byte) {
  664. assert.Equal(e.T, ICQLegacySrvSysMsgDone, v4SrvCommand(p))
  665. }).Return(nil).Once()
  666. }, nil),
  667. },
  668. })
  669. }
  670. func TestV4Handler_HandleGetInfo(t *testing.T) {
  671. runV4HandleCases(t, []v4HandleCase{
  672. {
  673. name: "get info",
  674. session: v4Session(testV4UIN),
  675. wire: defaultV4Pkt(ICQLegacyCmdUserGetInfo, v4TargetUINData(99999)),
  676. setup: v4CaseSetup(func(e v4HandleEnv) {
  677. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  678. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  679. Run(func(_ *LegacySession, p []byte) {
  680. assert.Equal(e.T, ICQLegacySrvInfoReply, v4SrvCommand(p))
  681. }).Return(nil).Once()
  682. }, nil),
  683. },
  684. })
  685. }
  686. func TestV4Handler_HandleInfoReq(t *testing.T) {
  687. runV4HandleCases(t, []v4HandleCase{
  688. {
  689. name: "get info",
  690. session: v4Session(testV4UIN),
  691. wire: defaultV4Pkt(ICQLegacyCmdInfoReq, v4TargetUINData(99999)),
  692. setup: v4CaseSetup(func(e v4HandleEnv) {
  693. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  694. Return(&LegacyUserSearchResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  695. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  696. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  697. Run(func(_ *LegacySession, p []byte) {
  698. assert.Equal(e.T, ICQLegacySrvInfoReply, v4SrvCommand(p))
  699. }).Return(nil).Once()
  700. }, nil),
  701. },
  702. })
  703. }
  704. func TestV4Handler_HandleExtInfoReq(t *testing.T) {
  705. runV4HandleCases(t, []v4HandleCase{
  706. {
  707. name: "get ext info",
  708. session: v4Session(testV4UIN),
  709. wire: defaultV4Pkt(ICQLegacyCmdExtInfoReq, v4TargetUINData(99999)),
  710. setup: v4CaseSetup(func(e v4HandleEnv) {
  711. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
  712. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  713. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  714. Run(func(_ *LegacySession, p []byte) {
  715. assert.Equal(e.T, ICQLegacySrvExtInfoReply, v4SrvCommand(p))
  716. }).Return(nil).Once()
  717. }, nil),
  718. },
  719. })
  720. }
  721. func TestV4Handler_HandleUpdateBasic(t *testing.T) {
  722. runV4HandleCases(t, []v4HandleCase{
  723. {
  724. name: "success",
  725. session: v4Session(testV4UIN),
  726. wire: defaultV4Pkt(ICQLegacyCmdSetBasicInfo, v4UpdateBasicData()),
  727. setup: v4CaseSetup(func(e v4HandleEnv) {
  728. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
  729. e.Service.EXPECT().SetAuthMode(mock.Anything, testV4UIN, false).Return(nil).Once()
  730. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  731. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  732. Run(func(_ *LegacySession, p []byte) {
  733. assert.Equal(e.T, ICQLegacySrvUpdatedBasicV4, v4SrvCommand(p))
  734. }).Return(nil).Once()
  735. }, nil),
  736. },
  737. })
  738. }
  739. func TestV4Handler_HandleUpdateDetail(t *testing.T) {
  740. runV4HandleCases(t, []v4HandleCase{
  741. {
  742. name: "success",
  743. session: v4Session(testV4UIN),
  744. wire: defaultV4Pkt(ICQLegacyCmdUpdateDetail, v4UpdateDetailData()),
  745. setup: v4CaseSetup(func(e v4HandleEnv) {
  746. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV4UIN).Return(testMinimalUser(), nil).Once()
  747. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
  748. e.Service.EXPECT().UpdateMoreInfo(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
  749. e.Service.EXPECT().SetNotes(mock.Anything, testV4UIN, "about me").Return(nil).Once()
  750. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  751. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  752. Run(func(_ *LegacySession, p []byte) {
  753. assert.Equal(e.T, ICQLegacySrvUpdatedDetail, v4SrvCommand(p))
  754. }).Return(nil).Once()
  755. }, nil),
  756. },
  757. })
  758. }
  759. func TestV4Handler_HandleSearchByUIN(t *testing.T) {
  760. runV4HandleCases(t, []v4HandleCase{
  761. {
  762. name: "found",
  763. session: v4Session(testV4UIN),
  764. wire: defaultV4Pkt(ICQLegacyCmdSearchUIN, v4TargetUINData(99999)),
  765. setup: v4CaseSetup(func(e v4HandleEnv) {
  766. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  767. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  768. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  769. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  770. }, nil),
  771. },
  772. {
  773. name: "not found",
  774. session: v4Session(testV4UIN),
  775. wire: defaultV4Pkt(ICQLegacyCmdSearchUIN, v4TargetUINData(99999)),
  776. setup: v4CaseSetup(func(e v4HandleEnv) {
  777. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  778. Return(&LegacyUserSearchResult{}, nil).Once()
  779. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  780. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  781. }, nil),
  782. },
  783. })
  784. }
  785. func TestV4Handler_HandleSearchByName(t *testing.T) {
  786. runV4HandleCases(t, []v4HandleCase{
  787. {
  788. name: "stub ends search",
  789. session: v4Session(testV4UIN),
  790. wire: defaultV4Pkt(ICQLegacyCmdSearchUser, v2SearchUserData(1, "alice")),
  791. setup: v4CaseSetup(func(e v4HandleEnv) {
  792. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  793. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  794. Run(func(_ *LegacySession, p []byte) {
  795. assert.Equal(e.T, ICQLegacySrvSearchDone, v4SrvCommand(p))
  796. }).Return(nil).Once()
  797. }, nil),
  798. },
  799. })
  800. }
  801. func TestV4Handler_HandleSearchStart(t *testing.T) {
  802. runV4HandleCases(t, []v4HandleCase{
  803. {
  804. name: "stub ACK",
  805. session: v4Session(testV4UIN),
  806. wire: defaultV4Pkt(ICQLegacyCmdSearchStart, nil),
  807. setup: v4CaseSetup(func(e v4HandleEnv) {
  808. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).
  809. Run(func(_ *net.UDPAddr, p []byte) {
  810. assert.Equal(e.T, ICQLegacySrvAck, v4SrvCommand(p))
  811. }).Return(nil).Once()
  812. }, nil),
  813. },
  814. })
  815. }
  816. func TestV4Handler_HandleVisibleInvisibleMeta(t *testing.T) {
  817. runV4HandleCases(t, []v4HandleCase{
  818. {
  819. name: "visible list stub",
  820. session: v4Session(testV4UIN),
  821. wire: defaultV4Pkt(ICQLegacyCmdVisibleList, v4ContactListData([]uint32{11111})),
  822. setup: v4CaseSetup(func(e v4HandleEnv) {
  823. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  824. }, nil),
  825. },
  826. {
  827. name: "invisible list stub",
  828. session: v4Session(testV4UIN),
  829. wire: defaultV4Pkt(ICQLegacyCmdInvisibleList, v4ContactListData([]uint32{22222})),
  830. setup: v4CaseSetup(func(e v4HandleEnv) {
  831. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  832. }, nil),
  833. },
  834. {
  835. name: "meta user stub",
  836. session: v4Session(testV4UIN),
  837. wire: defaultV4Pkt(ICQLegacyCmdMetaUser, nil),
  838. setup: v4CaseSetup(func(e v4HandleEnv) {
  839. e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  840. }, nil),
  841. },
  842. })
  843. }
  844. func TestV4Handler_sendUserOnline(t *testing.T) {
  845. const (
  846. onlineUIN = uint32(67890)
  847. userStatus = uint32(0x00000010)
  848. )
  849. sender := newMockPacketSender(t)
  850. svc := newMockLegacyService(t)
  851. recipient := v4Session(testV4UIN)
  852. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  853. if v4SrvCommand(p) != ICQLegacySrvUserOnline {
  854. return false
  855. }
  856. data := v4SrvData(p)
  857. return len(data) >= 25 &&
  858. binary.LittleEndian.Uint32(data[0:4]) == onlineUIN
  859. })).Return(nil).Once()
  860. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  861. assert.NoError(t, h.sendUserOnline(recipient, onlineUIN, userStatus))
  862. }
  863. func TestV4Handler_sendUserOffline(t *testing.T) {
  864. const offlineUIN = uint32(88888)
  865. sender := newMockPacketSender(t)
  866. svc := newMockLegacyService(t)
  867. recipient := v4Session(testV4UIN)
  868. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  869. data := v4SrvData(p)
  870. return v4SrvCommand(p) == ICQLegacySrvUserOffline &&
  871. len(data) == 4 &&
  872. binary.LittleEndian.Uint32(data[0:4]) == offlineUIN
  873. })).Return(nil).Once()
  874. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  875. assert.NoError(t, h.sendUserOffline(recipient, offlineUIN))
  876. }
  877. func TestV4Handler_sendUserStatus(t *testing.T) {
  878. const (
  879. changedUIN = uint32(88888)
  880. status = uint32(0x00010002)
  881. )
  882. sender := newMockPacketSender(t)
  883. svc := newMockLegacyService(t)
  884. recipient := v4Session(testV4UIN)
  885. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  886. data := v4SrvData(p)
  887. return v4SrvCommand(p) == ICQLegacySrvUserStatus &&
  888. len(data) == 8 &&
  889. binary.LittleEndian.Uint32(data[0:4]) == changedUIN &&
  890. binary.LittleEndian.Uint32(data[4:8]) == status
  891. })).Return(nil).Once()
  892. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  893. assert.NoError(t, h.sendUserStatus(recipient, changedUIN, status))
  894. }
  895. func TestV4Handler_sendOnlineMessage(t *testing.T) {
  896. const fromUIN = uint32(55555)
  897. sender := newMockPacketSender(t)
  898. svc := newMockLegacyService(t)
  899. recipient := v4Session(testV4UIN)
  900. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  901. data := v4SrvData(p)
  902. return v4SrvCommand(p) == ICQLegacySrvSysMsgOnline &&
  903. len(data) >= 8 &&
  904. binary.LittleEndian.Uint32(data[0:4]) == fromUIN
  905. })).Return(nil).Once()
  906. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  907. assert.NoError(t, h.sendOnlineMessage(recipient, fromUIN, 0x0001, "hello", testV4Seq2))
  908. }
  909. func TestV4Handler_sendSearchFoundAndEnd(t *testing.T) {
  910. sender := newMockPacketSender(t)
  911. svc := newMockLegacyService(t)
  912. sess := v4Session(testV4UIN)
  913. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  914. return v4SrvCommand(p) == ICQLegacySrvSearchFound
  915. })).Return(nil).Once()
  916. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  917. return v4SrvCommand(p) == ICQLegacySrvSearchDone
  918. })).Return(nil).Once()
  919. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  920. result := &LegacyUserSearchResult{UIN: 99999, Nickname: "alice"}
  921. assert.NoError(t, h.sendSearchFound(sess, testV4Seq2, result))
  922. assert.NoError(t, h.sendSearchEnd(sess, testV4Seq2, false))
  923. }
  924. func TestV4Handler_sendBasicInfo(t *testing.T) {
  925. sender := newMockPacketSender(t)
  926. svc := newMockLegacyService(t)
  927. sess := v4Session(testV4UIN)
  928. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  929. data := v4SrvData(p)
  930. return v4SrvCommand(p) == ICQLegacySrvInfoReply &&
  931. len(data) >= 4 &&
  932. binary.LittleEndian.Uint32(data[0:4]) == uint32(99999)
  933. })).Return(nil).Once()
  934. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  935. assert.NoError(t, h.sendBasicInfo(sess, testV4Seq2, 99999))
  936. }
  937. func TestV4Handler_sendBasicInfoResponse(t *testing.T) {
  938. sender := newMockPacketSender(t)
  939. svc := newMockLegacyService(t)
  940. sess := v4Session(testV4UIN)
  941. svc.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  942. Return(&LegacyUserSearchResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  943. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  944. return v4SrvCommand(p) == ICQLegacySrvInfoReply
  945. })).Return(nil).Once()
  946. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  947. assert.NoError(t, h.sendBasicInfoResponse(sess, testV4Seq2, 99999))
  948. }
  949. func TestV4Handler_sendExtInfoResponse(t *testing.T) {
  950. sender := newMockPacketSender(t)
  951. svc := newMockLegacyService(t)
  952. sess := v4Session(testV4UIN)
  953. svc.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
  954. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  955. return v4SrvCommand(p) == ICQLegacySrvExtInfoReply
  956. })).Return(nil).Once()
  957. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  958. assert.NoError(t, h.sendExtInfoResponse(sess, testV4Seq2, 99999))
  959. }
  960. func TestV4Handler_sendDeptsListWithCheckcode(t *testing.T) {
  961. sender := newMockPacketSender(t)
  962. svc := newMockLegacyService(t)
  963. sess := v4Session(testV4UIN)
  964. sender.EXPECT().SendToSession(sess, mock.MatchedBy(func(p []byte) bool {
  965. return v4SrvCommand(p) == ICQLegacySrvUserDepsList && len(p) == 28
  966. })).Return(nil).Once()
  967. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  968. assert.NoError(t, h.sendDeptsListWithCheckcode(sess, testV4Seq2))
  969. }
  970. func TestV4Handler_sendRegisterInfo(t *testing.T) {
  971. sender := newMockPacketSender(t)
  972. svc := newMockLegacyService(t)
  973. sender.EXPECT().SendPacket(testV4Addr, mock.MatchedBy(func(p []byte) bool {
  974. return v4SrvCommand(p) == ICQLegacySrvRegisterInfo
  975. })).Return(nil).Once()
  976. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  977. assert.NoError(t, h.sendRegisterInfo(testV4Addr, testV4Seq2, testV4UIN))
  978. }
  979. func TestV4Handler_sendRegistrationOK(t *testing.T) {
  980. const newUIN = uint32(100001)
  981. sender := newMockPacketSender(t)
  982. svc := newMockLegacyService(t)
  983. sender.EXPECT().SendPacket(testV4Addr, mock.MatchedBy(func(p []byte) bool {
  984. return v4SrvCommand(p) == ICQLegacySrvNewUIN &&
  985. binary.LittleEndian.Uint32(p[8:12]) == newUIN
  986. })).Return(nil).Once()
  987. h := newTestV4Handler(t, sender, svc, newTestLegacySessionManager())
  988. assert.NoError(t, h.sendRegistrationOK(testV4Addr, testV4Seq2, newUIN, "pass"))
  989. }
  990. func TestV4Handler_HandleLogoff_notifiesContacts(t *testing.T) {
  991. const contactUIN = uint32(88888)
  992. sender := newMockPacketSender(t)
  993. svc := newMockLegacyService(t)
  994. sessions := newTestLegacySessionManager()
  995. loggingOff := v4Session(testV4UIN)
  996. loggingOff.SetContactList([]uint32{contactUIN})
  997. contact := v4Session(contactUIN)
  998. contact.SetContactList([]uint32{testV4UIN})
  999. registerSession(sessions, loggingOff)
  1000. registerSession(sessions, contact)
  1001. svc.EXPECT().NotifyUserOffline(mock.Anything, testV4UIN).Return(nil).Once()
  1002. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1003. data := v4SrvData(p)
  1004. return v4SrvCommand(p) == ICQLegacySrvUserOffline &&
  1005. len(data) == 4 &&
  1006. binary.LittleEndian.Uint32(data[0:4]) == testV4UIN
  1007. })).Return(nil).Once()
  1008. h := newTestV4HandlerWithDispatcher(t, sender, svc, sessions)
  1009. pkt := buildV4ClientPacket(t, ICQLegacyCmdLogoff, testV4Seq, testV4Seq2, testV4UIN, nil)
  1010. assert.NoError(t, h.Handle(loggingOff, testV4Addr, pkt))
  1011. assert.Nil(t, sessions.GetSession(testV4UIN))
  1012. }
  1013. func TestV4Handler_HandleSetStatus_notifiesContacts(t *testing.T) {
  1014. const (
  1015. contactUIN = uint32(88888)
  1016. newStatus = uint32(0x00010002)
  1017. )
  1018. sender := newMockPacketSender(t)
  1019. svc := newMockLegacyService(t)
  1020. sessions := newTestLegacySessionManager()
  1021. changer := v4Session(testV4UIN)
  1022. contact := v4Session(contactUIN)
  1023. registerSession(sessions, changer)
  1024. registerSession(sessions, contact)
  1025. svc.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  1026. Return(&StatusChangeResult{
  1027. NotifyTargets: []NotifyTarget{{UIN: contactUIN}},
  1028. }, nil).Once()
  1029. sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1030. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1031. data := v4SrvData(p)
  1032. return v4SrvCommand(p) == ICQLegacySrvUserStatus &&
  1033. len(data) == 8 &&
  1034. binary.LittleEndian.Uint32(data[0:4]) == testV4UIN &&
  1035. binary.LittleEndian.Uint32(data[4:8]) == newStatus
  1036. })).Return(nil).Once()
  1037. h := newTestV4HandlerWithDispatcher(t, sender, svc, sessions)
  1038. statusData := make([]byte, 4)
  1039. binary.LittleEndian.PutUint32(statusData, newStatus)
  1040. pkt := buildV4ClientPacket(t, ICQLegacyCmdSetStatus, testV4Seq, testV4Seq2, testV4UIN, statusData)
  1041. assert.NoError(t, h.Handle(changer, testV4Addr, pkt))
  1042. assert.Equal(t, newStatus, changer.GetStatus())
  1043. }
  1044. func TestV4Handler_HandleUserAdd_notifyTarget(t *testing.T) {
  1045. const targetUIN = uint32(99999)
  1046. adder := v4Session(testV4UIN)
  1047. target := v4Session(targetUIN)
  1048. sender := newMockPacketSender(t)
  1049. svc := newMockLegacyService(t)
  1050. sessions := newTestLegacySessionManager()
  1051. registerSession(sessions, adder)
  1052. registerSession(sessions, target)
  1053. svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).Return(&UserAddResult{}, nil).Once()
  1054. sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1055. sender.EXPECT().SendToSession(adder, mock.MatchedBy(func(p []byte) bool {
  1056. return v4SrvCommand(p) == ICQLegacySrvUserOnline
  1057. })).Return(nil).Once()
  1058. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1059. return v4SrvCommand(p) == ICQLegacySrvSysMsgOnline
  1060. })).Return(nil).Once()
  1061. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1062. return v4SrvCommand(p) == ICQLegacySrvUserOnline
  1063. })).Return(nil).Once()
  1064. h := newTestV4Handler(t, sender, svc, sessions)
  1065. pkt := buildV4ClientPacket(t, ICQLegacyCmdUserAdd, testV4Seq, testV4Seq2, testV4UIN, v4TargetUINData(targetUIN))
  1066. assert.NoError(t, h.Handle(adder, testV4Addr, pkt))
  1067. assert.Contains(t, adder.GetContactList(), targetUIN)
  1068. }
  1069. func newTestV4HandlerWithDispatcher(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V4Handler {
  1070. t.Helper()
  1071. logger := slog.Default()
  1072. if sessions == nil {
  1073. sessions = newTestLegacySessionManager()
  1074. }
  1075. v1 := NewV1Handler(sessions, svc, sender, logger)
  1076. v2 := NewV2Handler(sessions, svc, sender, NewV2PacketBuilder(), logger)
  1077. v3 := NewV3Handler(sessions, svc, sender, NewV3PacketBuilder(sessions, nil), logger)
  1078. v4 := NewV4Handler(sessions, svc, sender, NewV4PacketBuilder(sessions, nil), logger)
  1079. v5 := NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), logger)
  1080. cfg := config.ICQLegacyConfig{SupportedVersions: []int{1, 2, 3, 4, 5}}
  1081. dispatcher := NewProtocolDispatcher(v1, v2, v3, v4, v5, cfg, logger)
  1082. v4.SetDispatcher(dispatcher)
  1083. return v4
  1084. }