v3_handler_test.go 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  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. testV3UIN = uint32(12345)
  18. testV3Seq = uint16(42)
  19. testV3Seq2 = uint16(7)
  20. )
  21. var testV3Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4000}
  22. type v3HandleEnv struct {
  23. T *testing.T
  24. Sender *mockPacketSender
  25. Service *mockLegacyService
  26. Sessions *LegacySessionManager
  27. Session *LegacySession
  28. }
  29. type v3HandleCase struct {
  30. name string
  31. session *LegacySession
  32. pkt []byte
  33. wire func(t *testing.T) []byte
  34. setup func(v3HandleEnv) func(error)
  35. }
  36. func v3CaseSetup(pre func(v3HandleEnv), after func(e v3HandleEnv, err error)) func(v3HandleEnv) func(error) {
  37. return func(e v3HandleEnv) 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 runV3HandleCases(t *testing.T, cases []v3HandleCase) {
  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 := v3HandleEnv{
  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 := newTestV3Handler(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, testV3Addr, wire))
  81. })
  82. }
  83. }
  84. func newTestV3Handler(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V3Handler {
  85. t.Helper()
  86. if sessions == nil {
  87. sessions = newTestLegacySessionManager()
  88. }
  89. return NewV3Handler(sessions, svc, sender, NewV3PacketBuilder(sessions, nil), slog.Default())
  90. }
  91. func newTestV3HandlerWithDispatcher(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V3Handler {
  92. t.Helper()
  93. logger := slog.Default()
  94. if sessions == nil {
  95. sessions = newTestLegacySessionManager()
  96. }
  97. v1 := NewV1Handler(sessions, svc, sender, logger)
  98. v2 := NewV2Handler(sessions, svc, sender, NewV2PacketBuilder(), logger)
  99. v3 := NewV3Handler(sessions, svc, sender, NewV3PacketBuilder(sessions, nil), logger)
  100. v4 := NewV4Handler(sessions, svc, sender, NewV4PacketBuilder(sessions, nil), logger)
  101. v5 := NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), logger)
  102. cfg := config.ICQLegacyConfig{SupportedVersions: []int{1, 2, 3, 4, 5}}
  103. dispatcher := NewProtocolDispatcher(v1, v2, v3, v4, v5, cfg, logger)
  104. v3.SetDispatcher(dispatcher)
  105. return v3
  106. }
  107. func v3Session(uin uint32) *LegacySession {
  108. s := newTestLegacySession(uin, legacySessionOptOSCARSess, func(ls *LegacySession) {
  109. ls.Version = ICQLegacyVersionV3
  110. ls.SeqNumClient = testV3Seq
  111. })
  112. return s
  113. }
  114. func v3ServerCommand(packet []byte) uint16 {
  115. if len(packet) < 4 {
  116. return 0
  117. }
  118. return binary.LittleEndian.Uint16(packet[2:4])
  119. }
  120. func expectV3NotConnected(sender *mockPacketSender) {
  121. sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  122. Run(func(_ *net.UDPAddr, p []byte) {
  123. if got := v3ServerCommand(p); got != ICQLegacySrvNotConnected {
  124. panic(fmt.Sprintf("got command 0x%04X, want NOT_CONNECTED", got))
  125. }
  126. }).Return(nil).Once()
  127. }
  128. func buildV3ClientPacket(command, seq1, seq2 uint16, uin uint32, data []byte) []byte {
  129. buf := make([]byte, 12+len(data))
  130. binary.LittleEndian.PutUint16(buf[0:2], ICQLegacyVersionV3)
  131. binary.LittleEndian.PutUint16(buf[2:4], command)
  132. binary.LittleEndian.PutUint16(buf[4:6], seq1)
  133. binary.LittleEndian.PutUint16(buf[6:8], seq2)
  134. binary.LittleEndian.PutUint32(buf[8:12], uin)
  135. copy(buf[12:], data)
  136. return buf
  137. }
  138. func defaultV3Pkt(command uint16, data []byte) []byte {
  139. return buildV3ClientPacket(command, testV3Seq, testV3Seq2, testV3UIN, data)
  140. }
  141. func v3LoginData(password string) []byte {
  142. pwd := []byte(password)
  143. buf := make([]byte, 4+4+2+len(pwd))
  144. binary.LittleEndian.PutUint32(buf[0:4], 0)
  145. binary.LittleEndian.PutUint32(buf[4:8], 4000)
  146. binary.LittleEndian.PutUint16(buf[8:10], uint16(len(pwd)))
  147. copy(buf[10:], pwd)
  148. return buf
  149. }
  150. func v3GetDepsPayload(uin uint32, password string) []byte {
  151. pwd := []byte(password)
  152. buf := make([]byte, 4+2+len(pwd))
  153. binary.LittleEndian.PutUint32(buf[0:4], uin)
  154. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
  155. copy(buf[6:], pwd)
  156. return buf
  157. }
  158. func v3GetDepsData(uin uint32, password string) []byte {
  159. return append(make([]byte, 4), v3GetDepsPayload(uin, password)...)
  160. }
  161. func v3TimestampPrefix(payload []byte) []byte {
  162. return append(make([]byte, 4), payload...)
  163. }
  164. func v3LPString(s string) []byte {
  165. buf := new(bytes.Buffer)
  166. writeLegacyString(buf, s)
  167. return buf.Bytes()
  168. }
  169. func v3ContactListPayload(uins []uint32) []byte {
  170. buf := make([]byte, 1+4*len(uins))
  171. buf[0] = byte(len(uins))
  172. for i, u := range uins {
  173. binary.LittleEndian.PutUint32(buf[1+4*i:], u)
  174. }
  175. return buf
  176. }
  177. func v3ContactListData(uins []uint32) []byte {
  178. return v3TimestampPrefix(v3ContactListPayload(uins))
  179. }
  180. func v3MessagePayload(toUIN uint32, msgType uint16, message string) []byte {
  181. msg := []byte(message)
  182. buf := make([]byte, 4+2+2+len(msg))
  183. binary.LittleEndian.PutUint32(buf[0:4], toUIN)
  184. binary.LittleEndian.PutUint16(buf[4:6], msgType)
  185. binary.LittleEndian.PutUint16(buf[6:8], uint16(len(msg)))
  186. copy(buf[8:], msg)
  187. return buf
  188. }
  189. func v3MessageData(toUIN uint32, msgType uint16, message string) []byte {
  190. return v3TimestampPrefix(v3MessagePayload(toUIN, msgType, message))
  191. }
  192. func v3TargetUINPayload(target uint32) []byte {
  193. buf := make([]byte, 4)
  194. binary.LittleEndian.PutUint32(buf, target)
  195. return buf
  196. }
  197. func v3TargetUINData(target uint32) []byte {
  198. return v3TimestampPrefix(v3TargetUINPayload(target))
  199. }
  200. func v3SetStatusData(status uint32) []byte {
  201. buf := make([]byte, 8)
  202. binary.LittleEndian.PutUint32(buf[4:8], status)
  203. return buf
  204. }
  205. func v3SearchUINData(targetUIN uint32) []byte {
  206. uinStr := fmt.Sprintf("%d", targetUIN)
  207. buf := make([]byte, 4+2+2+2+len(uinStr)+1)
  208. off := 4
  209. binary.LittleEndian.PutUint16(buf[off:], 0x00FF)
  210. off += 2
  211. binary.LittleEndian.PutUint16(buf[off:], 0x0002)
  212. off += 2
  213. binary.LittleEndian.PutUint16(buf[off:], uint16(len(uinStr)+1))
  214. off += 2
  215. copy(buf[off:], uinStr)
  216. return buf
  217. }
  218. func v3SetBasicInfoData(nick, first, last, email string) []byte {
  219. buf := new(bytes.Buffer)
  220. buf.Write(make([]byte, 4))
  221. writeLegacyString(buf, nick)
  222. writeLegacyString(buf, first)
  223. writeLegacyString(buf, last)
  224. writeLegacyString(buf, email)
  225. return buf.Bytes()
  226. }
  227. func v3SetNotesData(notes string) []byte {
  228. n := []byte(notes)
  229. buf := make([]byte, 4+2+len(n))
  230. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(n)))
  231. copy(buf[6:], n)
  232. return buf
  233. }
  234. func v3SetPasswordData(password string) []byte {
  235. pwd := []byte(password)
  236. buf := make([]byte, 4+2+len(pwd))
  237. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
  238. copy(buf[6:], pwd)
  239. return buf
  240. }
  241. func v3SetAuthData(authRequired bool) []byte {
  242. buf := make([]byte, 5)
  243. if authRequired {
  244. buf[4] = 1
  245. }
  246. return buf
  247. }
  248. func v3SetStateData(status, estatus uint32) []byte {
  249. buf := make([]byte, 12)
  250. binary.LittleEndian.PutUint32(buf[4:8], status)
  251. binary.LittleEndian.PutUint32(buf[8:12], estatus)
  252. return buf
  253. }
  254. func v3ServerPacketData(p []byte) []byte {
  255. if len(p) < 16 {
  256. return nil
  257. }
  258. return p[16:]
  259. }
  260. func TestV3Handler_Handle(t *testing.T) {
  261. runV3HandleCases(t, []v3HandleCase{
  262. {
  263. name: "packet too short",
  264. pkt: []byte{0x01, 0x02},
  265. setup: v3CaseSetup(nil, func(e v3HandleEnv, err error) {
  266. require.Error(e.T, err)
  267. assert.Contains(e.T, err.Error(), "packet too short")
  268. }),
  269. },
  270. {
  271. name: "nil session disallowed command",
  272. session: nil,
  273. pkt: defaultV3Pkt(ICQLegacyCmdKeepAlive, nil),
  274. setup: v3CaseSetup(func(e v3HandleEnv) {
  275. expectV3NotConnected(e.Sender)
  276. }, nil),
  277. },
  278. {
  279. name: "nil session ACK allowed",
  280. session: nil,
  281. pkt: defaultV3Pkt(ICQLegacyCmdAck, nil),
  282. },
  283. {
  284. name: "session state updated",
  285. session: v3Session(testV3UIN),
  286. pkt: defaultV3Pkt(ICQLegacyCmdAck, nil),
  287. setup: v3CaseSetup(nil, func(e v3HandleEnv, err error) {
  288. assert.NoError(e.T, err)
  289. assert.Equal(e.T, testV3Seq, e.Session.SeqNumClient)
  290. assert.WithinDuration(e.T, time.Now(), e.Session.GetLastActivity(), time.Second)
  291. }),
  292. },
  293. {
  294. name: "unknown command with session",
  295. session: v3Session(testV3UIN),
  296. pkt: defaultV3Pkt(0xFFFF, nil),
  297. setup: v3CaseSetup(func(e v3HandleEnv) {
  298. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  299. Run(func(_ *net.UDPAddr, p []byte) {
  300. assert.Equal(e.T, ICQLegacySrvAck, v3ServerCommand(p))
  301. }).Return(nil).Once()
  302. }, nil),
  303. },
  304. })
  305. }
  306. func TestV3Handler_HandleFirstLogin(t *testing.T) {
  307. runV3HandleCases(t, []v3HandleCase{
  308. {
  309. name: "first login",
  310. session: nil,
  311. pkt: defaultV3Pkt(ICQLegacyCmdFirstLogin, nil),
  312. setup: v3CaseSetup(func(e v3HandleEnv) {
  313. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  314. Run(func(_ *net.UDPAddr, p []byte) {
  315. assert.Equal(e.T, ICQLegacySrvAck, v3ServerCommand(p))
  316. }).Return(nil).Once()
  317. }, nil),
  318. },
  319. })
  320. }
  321. func TestV3Handler_HandleGetDeps(t *testing.T) {
  322. runV3HandleCases(t, []v3HandleCase{
  323. {
  324. name: "valid credentials",
  325. pkt: defaultV3Pkt(ICQLegacyCmdGetDeps, v3GetDepsData(testV3UIN, "secret")),
  326. setup: v3CaseSetup(func(e v3HandleEnv) {
  327. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV3UIN, "secret").Return(true, nil).Once()
  328. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  329. }, nil),
  330. },
  331. {
  332. name: "invalid credentials",
  333. pkt: defaultV3Pkt(ICQLegacyCmdGetDeps, v3GetDepsData(testV3UIN, "wrong")),
  334. setup: v3CaseSetup(func(e v3HandleEnv) {
  335. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV3UIN, "wrong").Return(false, nil).Once()
  336. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  337. Run(func(_ *net.UDPAddr, p []byte) {
  338. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v3ServerCommand(p))
  339. }).Return(nil).Once()
  340. }, nil),
  341. },
  342. {
  343. name: "service error",
  344. pkt: defaultV3Pkt(ICQLegacyCmdGetDeps, v3GetDepsData(testV3UIN, "secret")),
  345. setup: v3CaseSetup(func(e v3HandleEnv) {
  346. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV3UIN, "secret").
  347. Return(false, errors.New("db down")).Once()
  348. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  349. Run(func(_ *net.UDPAddr, p []byte) {
  350. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v3ServerCommand(p))
  351. }).Return(nil).Once()
  352. }, nil),
  353. },
  354. })
  355. }
  356. func TestV3Handler_HandleLogin(t *testing.T) {
  357. runV3HandleCases(t, []v3HandleCase{
  358. {
  359. name: "auth success",
  360. session: nil,
  361. pkt: defaultV3Pkt(ICQLegacyCmdLogin, v3LoginData("secret")),
  362. setup: v3CaseSetup(func(e v3HandleEnv) {
  363. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  364. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
  365. return req.UIN == testV3UIN && req.Password == "secret"
  366. })).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
  367. e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
  368. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
  369. }, func(e v3HandleEnv, err error) {
  370. assert.NoError(e.T, err)
  371. assert.NotNil(e.T, e.Sessions.GetSession(testV3UIN))
  372. }),
  373. },
  374. {
  375. name: "bad password",
  376. session: nil,
  377. pkt: defaultV3Pkt(ICQLegacyCmdLogin, v3LoginData("wrong")),
  378. setup: v3CaseSetup(func(e v3HandleEnv) {
  379. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  380. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
  381. Return(&AuthResult{Success: false, ErrorCode: 0x0001}, nil).Once()
  382. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  383. Run(func(_ *net.UDPAddr, p []byte) {
  384. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v3ServerCommand(p))
  385. }).Return(nil).Once()
  386. }, nil),
  387. },
  388. {
  389. name: "parse error",
  390. session: nil,
  391. pkt: defaultV3Pkt(ICQLegacyCmdLogin, []byte{0, 0, 0}),
  392. setup: v3CaseSetup(func(e v3HandleEnv) {
  393. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  394. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  395. Run(func(_ *net.UDPAddr, p []byte) {
  396. assert.Equal(e.T, ICQLegacySrvWrongPasswd, v3ServerCommand(p))
  397. }).Return(nil).Once()
  398. }, nil),
  399. },
  400. })
  401. }
  402. func TestV3Handler_HandleContactList(t *testing.T) {
  403. runV3HandleCases(t, []v3HandleCase{
  404. {
  405. name: "valid contact list",
  406. session: v3Session(testV3UIN),
  407. pkt: defaultV3Pkt(ICQLegacyCmdContactList, v3ContactListData([]uint32{99999})),
  408. setup: v3CaseSetup(func(e v3HandleEnv) {
  409. e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
  410. Return(&ContactListResult{}, nil).Once()
  411. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  412. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  413. }, nil),
  414. },
  415. {
  416. name: "nil session",
  417. pkt: defaultV3Pkt(ICQLegacyCmdContactList, v3ContactListData(nil)),
  418. setup: v3CaseSetup(func(e v3HandleEnv) {
  419. expectV3NotConnected(e.Sender)
  420. }, nil),
  421. },
  422. })
  423. }
  424. func TestV3Handler_HandlePing(t *testing.T) {
  425. runV3HandleCases(t, []v3HandleCase{
  426. {
  427. name: "keep alive",
  428. session: v3Session(testV3UIN),
  429. pkt: defaultV3Pkt(ICQLegacyCmdKeepAlive, nil),
  430. setup: v3CaseSetup(func(e v3HandleEnv) {
  431. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  432. Run(func(_ *net.UDPAddr, p []byte) {
  433. assert.Equal(e.T, ICQLegacySrvAck, v3ServerCommand(p))
  434. }).Return(nil).Once()
  435. }, nil),
  436. },
  437. {
  438. name: "keep alive 2",
  439. session: v3Session(testV3UIN),
  440. pkt: defaultV3Pkt(ICQLegacyCmdKeepAlive2, nil),
  441. setup: v3CaseSetup(func(e v3HandleEnv) {
  442. e.Sender.EXPECT().SendPacket(testV3Addr, mock.AnythingOfType("[]uint8")).
  443. Run(func(_ *net.UDPAddr, p []byte) {
  444. assert.Equal(e.T, ICQLegacySrvAck, v3ServerCommand(p))
  445. }).Return(nil).Once()
  446. }, nil),
  447. },
  448. {
  449. name: "nil session",
  450. pkt: defaultV3Pkt(ICQLegacyCmdKeepAlive, nil),
  451. setup: v3CaseSetup(func(e v3HandleEnv) {
  452. expectV3NotConnected(e.Sender)
  453. }, nil),
  454. },
  455. })
  456. }
  457. func TestV3Handler_HandleLogoff(t *testing.T) {
  458. runV3HandleCases(t, []v3HandleCase{
  459. {
  460. name: "active session",
  461. session: v3Session(testV3UIN),
  462. pkt: defaultV3Pkt(ICQLegacyCmdLogoff, nil),
  463. setup: v3CaseSetup(func(e v3HandleEnv) {
  464. registerSession(e.Sessions, e.Session)
  465. e.Service.EXPECT().NotifyUserOffline(mock.Anything, testV3UIN).Return(nil).Once()
  466. }, func(e v3HandleEnv, err error) {
  467. assert.NoError(e.T, err)
  468. assert.Nil(e.T, e.Sessions.GetSession(testV3UIN))
  469. }),
  470. },
  471. {
  472. name: "nil session",
  473. pkt: defaultV3Pkt(ICQLegacyCmdLogoff, nil),
  474. setup: v3CaseSetup(func(e v3HandleEnv) {
  475. expectV3NotConnected(e.Sender)
  476. }, nil),
  477. },
  478. })
  479. }
  480. func TestV3Handler_HandleSetStatus(t *testing.T) {
  481. runV3HandleCases(t, []v3HandleCase{
  482. {
  483. name: "valid status",
  484. session: v3Session(testV3UIN),
  485. pkt: defaultV3Pkt(ICQLegacyCmdSetStatus, v3SetStatusData(0x00000001)),
  486. setup: v3CaseSetup(func(e v3HandleEnv) {
  487. e.Service.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  488. Return(&StatusChangeResult{}, nil).Once()
  489. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  490. }, func(e v3HandleEnv, err error) {
  491. assert.NoError(e.T, err)
  492. assert.Equal(e.T, uint32(0x00000001), e.Session.GetStatus())
  493. }),
  494. },
  495. {
  496. name: "nil session",
  497. pkt: defaultV3Pkt(ICQLegacyCmdSetStatus, v3SetStatusData(1)),
  498. setup: v3CaseSetup(func(e v3HandleEnv) {
  499. expectV3NotConnected(e.Sender)
  500. }, nil),
  501. },
  502. })
  503. }
  504. func TestV3Handler_HandleMessage(t *testing.T) {
  505. target := v3Session(99999)
  506. runV3HandleCases(t, []v3HandleCase{
  507. {
  508. name: "offline stored",
  509. session: v3Session(testV3UIN),
  510. pkt: defaultV3Pkt(ICQLegacyCmdThruServer, v3MessageData(99999, 0x0001, "hi")),
  511. setup: v3CaseSetup(func(e v3HandleEnv) {
  512. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  513. Return(&MessageResult{StoredOffline: true}, nil).Once()
  514. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  515. }, nil),
  516. },
  517. {
  518. name: "online target",
  519. session: v3Session(testV3UIN),
  520. pkt: defaultV3Pkt(ICQLegacyCmdThruServer, v3MessageData(99999, 0x0001, "hi")),
  521. setup: v3CaseSetup(func(e v3HandleEnv) {
  522. registerSession(e.Sessions, target)
  523. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  524. Return(&MessageResult{TargetOnline: true}, nil).Once()
  525. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  526. e.Sender.EXPECT().SendToSession(target, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  527. }, nil),
  528. },
  529. {
  530. name: "nil session",
  531. pkt: defaultV3Pkt(ICQLegacyCmdThruServer, v3MessageData(99999, 0x0001, "hi")),
  532. setup: v3CaseSetup(func(e v3HandleEnv) {
  533. expectV3NotConnected(e.Sender)
  534. }, nil),
  535. },
  536. })
  537. }
  538. func TestV3Handler_HandleAuthorize(t *testing.T) {
  539. runV3HandleCases(t, []v3HandleCase{
  540. {
  541. name: "authorize message",
  542. session: v3Session(testV3UIN),
  543. pkt: defaultV3Pkt(ICQLegacyCmdAuthorize, v3MessageData(99999, 0x0001, "auth")),
  544. setup: v3CaseSetup(func(e v3HandleEnv) {
  545. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  546. Return(&MessageResult{StoredOffline: true}, nil).Once()
  547. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  548. }, nil),
  549. },
  550. })
  551. }
  552. func TestV3Handler_HandleUserAdd(t *testing.T) {
  553. runV3HandleCases(t, []v3HandleCase{
  554. {
  555. name: "success",
  556. session: v3Session(testV3UIN),
  557. pkt: defaultV3Pkt(ICQLegacyCmdUserAdd, v3TargetUINData(99999)),
  558. setup: v3CaseSetup(func(e v3HandleEnv) {
  559. e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  560. Return(&UserAddResult{}, nil).Once()
  561. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  562. }, func(e v3HandleEnv, err error) {
  563. assert.Contains(e.T, e.Session.GetContactList(), uint32(99999))
  564. }),
  565. },
  566. {
  567. name: "nil session",
  568. pkt: defaultV3Pkt(ICQLegacyCmdUserAdd, v3TargetUINData(99999)),
  569. setup: v3CaseSetup(func(e v3HandleEnv) {
  570. expectV3NotConnected(e.Sender)
  571. }, nil),
  572. },
  573. })
  574. }
  575. func TestV3Handler_HandleOfflineMsgReq(t *testing.T) {
  576. runV3HandleCases(t, []v3HandleCase{
  577. {
  578. name: "no messages",
  579. session: v3Session(testV3UIN),
  580. pkt: defaultV3Pkt(ICQLegacyCmdSysMsgReq, nil),
  581. setup: v3CaseSetup(func(e v3HandleEnv) {
  582. e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV3UIN).
  583. Return([]LegacyOfflineMessage{}, nil).Once()
  584. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  585. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  586. }, nil),
  587. },
  588. })
  589. }
  590. func TestV3Handler_HandleSetBasicInfo(t *testing.T) {
  591. runV3HandleCases(t, []v3HandleCase{
  592. {
  593. name: "success",
  594. session: v3Session(testV3UIN),
  595. pkt: defaultV3Pkt(ICQLegacyCmdSetBasicInfo, v3SetBasicInfoData("nick", "first", "last", "a@b.com")),
  596. setup: v3CaseSetup(func(e v3HandleEnv) {
  597. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
  598. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  599. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  600. }, nil),
  601. },
  602. })
  603. }
  604. func TestV3Handler_HandleSetHomeInfo(t *testing.T) {
  605. runV3HandleCases(t, []v3HandleCase{
  606. {
  607. name: "data too short",
  608. session: v3Session(testV3UIN),
  609. pkt: defaultV3Pkt(0x0582, []byte{0, 0, 0}),
  610. setup: v3CaseSetup(func(e v3HandleEnv) {
  611. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  612. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  613. }, nil),
  614. },
  615. {
  616. name: "updates profile",
  617. session: v3Session(testV3UIN),
  618. pkt: defaultV3Pkt(0x0582, v3SetBasicInfoData("addr", "city", "CA", "555")),
  619. setup: v3CaseSetup(func(e v3HandleEnv) {
  620. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV3UIN).Return(testMinimalUser(), nil).Once()
  621. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
  622. e.Service.EXPECT().SetAuthMode(mock.Anything, testV3UIN, true).Return(nil).Once()
  623. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  624. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  625. }, nil),
  626. },
  627. })
  628. }
  629. func TestV3Handler_HandleSetHomeWeb(t *testing.T) {
  630. runV3HandleCases(t, []v3HandleCase{
  631. {
  632. name: "success",
  633. session: v3Session(testV3UIN),
  634. pkt: defaultV3Pkt(0x058C, append(make([]byte, 4), 0, 0, 0, 2, 0, 'h', 'p')),
  635. setup: v3CaseSetup(func(e v3HandleEnv) {
  636. e.Service.EXPECT().UpdateMoreInfo(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
  637. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  638. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  639. }, nil),
  640. },
  641. })
  642. }
  643. func TestV3Handler_HandleSetWorkInfo(t *testing.T) {
  644. runV3HandleCases(t, []v3HandleCase{
  645. {
  646. name: "data too short",
  647. session: v3Session(testV3UIN),
  648. pkt: defaultV3Pkt(0x0578, []byte{0, 0, 0}),
  649. setup: v3CaseSetup(func(e v3HandleEnv) {
  650. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  651. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  652. }, nil),
  653. },
  654. })
  655. }
  656. func TestV3Handler_HandleSetWorkWeb(t *testing.T) {
  657. runV3HandleCases(t, []v3HandleCase{
  658. {
  659. name: "success",
  660. session: v3Session(testV3UIN),
  661. pkt: defaultV3Pkt(0x05BE, v3TimestampPrefix(v3LPString("http://work.example"))),
  662. setup: v3CaseSetup(func(e v3HandleEnv) {
  663. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV3UIN).Return(testMinimalUser(), nil).Once()
  664. e.Service.EXPECT().UpdateWorkInfo(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
  665. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  666. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  667. }, nil),
  668. },
  669. })
  670. }
  671. func TestV3Handler_HandleUnknownDep(t *testing.T) {
  672. runV3HandleCases(t, []v3HandleCase{
  673. {
  674. name: "acks request",
  675. session: v3Session(testV3UIN),
  676. pkt: defaultV3Pkt(0x0604, nil),
  677. setup: v3CaseSetup(func(e v3HandleEnv) {
  678. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  679. }, nil),
  680. },
  681. })
  682. }
  683. func TestV3Handler_HandleVisibleList(t *testing.T) {
  684. runV3HandleCases(t, []v3HandleCase{
  685. {
  686. name: "acks list",
  687. session: v3Session(testV3UIN),
  688. pkt: defaultV3Pkt(0x06AE, v3ContactListData([]uint32{11111})),
  689. setup: v3CaseSetup(func(e v3HandleEnv) {
  690. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  691. }, nil),
  692. },
  693. })
  694. }
  695. func TestV3Handler_HandleInvisibleList(t *testing.T) {
  696. runV3HandleCases(t, []v3HandleCase{
  697. {
  698. name: "acks list",
  699. session: v3Session(testV3UIN),
  700. pkt: defaultV3Pkt(0x06A4, v3ContactListData([]uint32{22222})),
  701. setup: v3CaseSetup(func(e v3HandleEnv) {
  702. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  703. }, nil),
  704. },
  705. })
  706. }
  707. func TestV3Handler_HandleSearchStart(t *testing.T) {
  708. runV3HandleCases(t, []v3HandleCase{
  709. {
  710. name: "found",
  711. session: v3Session(testV3UIN),
  712. pkt: defaultV3Pkt(ICQLegacyCmdSearchStart, v3SearchUINData(99999)),
  713. setup: v3CaseSetup(func(e v3HandleEnv) {
  714. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  715. Return(&LegacyUserSearchResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  716. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  717. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  718. }, nil),
  719. },
  720. {
  721. name: "not found",
  722. session: v3Session(testV3UIN),
  723. pkt: defaultV3Pkt(ICQLegacyCmdSearchStart, v3SearchUINData(99999)),
  724. setup: v3CaseSetup(func(e v3HandleEnv) {
  725. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  726. Return(nil, nil).Once()
  727. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  728. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  729. }, nil),
  730. },
  731. })
  732. }
  733. func TestV3Handler_HandleGetDeps1(t *testing.T) {
  734. runV3HandleCases(t, []v3HandleCase{
  735. {
  736. name: "sends deps list",
  737. session: v3Session(testV3UIN),
  738. pkt: defaultV3Pkt(ICQLegacyCmdGetDeps1, nil),
  739. setup: v3CaseSetup(func(e v3HandleEnv) {
  740. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  741. e.Sender.EXPECT().SendToSession(e.Session, mock.MatchedBy(func(p []byte) bool {
  742. return v3ServerCommand(p) == ICQLegacySrvUserDepsList1
  743. })).Return(nil).Once()
  744. }, nil),
  745. },
  746. })
  747. }
  748. func TestV3Handler_HandleGetNotes(t *testing.T) {
  749. runV3HandleCases(t, []v3HandleCase{
  750. {
  751. name: "success",
  752. session: v3Session(testV3UIN),
  753. pkt: defaultV3Pkt(0x05AA, v3TargetUINData(testV3UIN)),
  754. setup: v3CaseSetup(func(e v3HandleEnv) {
  755. e.Service.EXPECT().GetNotes(mock.Anything, testV3UIN).Return("hello notes", nil).Once()
  756. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  757. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  758. }, nil),
  759. },
  760. })
  761. }
  762. func TestV3Handler_HandleSetNotes(t *testing.T) {
  763. runV3HandleCases(t, []v3HandleCase{
  764. {
  765. name: "success",
  766. session: v3Session(testV3UIN),
  767. pkt: defaultV3Pkt(0x0596, v3SetNotesData("about me")),
  768. setup: v3CaseSetup(func(e v3HandleEnv) {
  769. e.Service.EXPECT().SetNotes(mock.Anything, testV3UIN, "about me").Return(nil).Once()
  770. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  771. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  772. }, nil),
  773. },
  774. })
  775. }
  776. func TestV3Handler_HandleSetPassword(t *testing.T) {
  777. runV3HandleCases(t, []v3HandleCase{
  778. {
  779. name: "success",
  780. session: v3Session(testV3UIN),
  781. pkt: defaultV3Pkt(ICQLegacyCmdSetPassword, v3SetPasswordData("newsecret")),
  782. setup: v3CaseSetup(func(e v3HandleEnv) {
  783. e.Service.EXPECT().SetPassword(mock.Anything, testV3UIN, "", "newsecret").Return(nil).Once()
  784. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  785. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  786. }, nil),
  787. },
  788. })
  789. }
  790. func TestV3Handler_HandleSetAuth(t *testing.T) {
  791. runV3HandleCases(t, []v3HandleCase{
  792. {
  793. name: "auth required",
  794. session: v3Session(testV3UIN),
  795. pkt: defaultV3Pkt(ICQLegacyCmdSetAuth, v3SetAuthData(true)),
  796. setup: v3CaseSetup(func(e v3HandleEnv) {
  797. e.Service.EXPECT().SetAuthMode(mock.Anything, testV3UIN, true).Return(nil).Once()
  798. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  799. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  800. }, nil),
  801. },
  802. })
  803. }
  804. func TestV3Handler_HandleSetState(t *testing.T) {
  805. runV3HandleCases(t, []v3HandleCase{
  806. {
  807. name: "updates status",
  808. session: v3Session(testV3UIN),
  809. pkt: defaultV3Pkt(0x0528, v3SetStateData(2, 1)),
  810. setup: v3CaseSetup(func(e v3HandleEnv) {
  811. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  812. }, func(e v3HandleEnv, err error) {
  813. assert.NoError(e.T, err)
  814. assert.Equal(e.T, uint32(2|1<<16), e.Session.GetStatus())
  815. }),
  816. },
  817. })
  818. }
  819. func TestV3Handler_HandleUsageStats(t *testing.T) {
  820. runV3HandleCases(t, []v3HandleCase{
  821. {
  822. name: "acks stats",
  823. session: v3Session(testV3UIN),
  824. pkt: defaultV3Pkt(0x0532, nil),
  825. setup: v3CaseSetup(func(e v3HandleEnv) {
  826. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  827. }, nil),
  828. },
  829. })
  830. }
  831. func TestV3Handler_HandleGetExternals(t *testing.T) {
  832. runV3HandleCases(t, []v3HandleCase{
  833. {
  834. name: "empty externals",
  835. session: v3Session(testV3UIN),
  836. pkt: defaultV3Pkt(ICQLegacyCmdGetExternals, nil),
  837. setup: v3CaseSetup(func(e v3HandleEnv) {
  838. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  839. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  840. }, nil),
  841. },
  842. })
  843. }
  844. func TestV3Handler_HandleSysAck(t *testing.T) {
  845. runV3HandleCases(t, []v3HandleCase{
  846. {
  847. name: "acks offline messages",
  848. session: v3Session(testV3UIN),
  849. pkt: defaultV3Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
  850. setup: v3CaseSetup(func(e v3HandleEnv) {
  851. e.Service.EXPECT().AckOfflineMessages(mock.Anything, testV3UIN).Return(nil).Once()
  852. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  853. }, nil),
  854. },
  855. })
  856. }
  857. func TestV3Handler_HandleOnlineInfo(t *testing.T) {
  858. runV3HandleCases(t, []v3HandleCase{
  859. {
  860. name: "reconnect",
  861. session: v3Session(testV3UIN),
  862. pkt: defaultV3Pkt(ICQLegacyCmdReconnect, nil),
  863. setup: v3CaseSetup(func(e v3HandleEnv) {
  864. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  865. }, nil),
  866. },
  867. })
  868. }
  869. func TestV3Handler_HandleGetInfo1(t *testing.T) {
  870. runV3HandleCases(t, []v3HandleCase{
  871. {
  872. name: "basic info only",
  873. session: v3Session(testV3UIN),
  874. pkt: defaultV3Pkt(ICQLegacyCmdInfoReq, v3TargetUINData(99999)),
  875. setup: v3CaseSetup(func(e v3HandleEnv) {
  876. e.Service.EXPECT().GetUserInfoForProtocol(mock.Anything, uint32(99999)).
  877. Return(&UserInfoResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  878. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  879. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  880. }, nil),
  881. },
  882. })
  883. }
  884. func TestV3Handler_HandleGetInfo(t *testing.T) {
  885. runV3HandleCases(t, []v3HandleCase{
  886. {
  887. name: "full info",
  888. session: v3Session(testV3UIN),
  889. pkt: defaultV3Pkt(ICQLegacyCmdUserGetInfo, v3TargetUINData(99999)),
  890. setup: v3CaseSetup(func(e v3HandleEnv) {
  891. e.Service.EXPECT().GetUserInfoForProtocol(mock.Anything, uint32(99999)).
  892. Return(&UserInfoResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  893. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  894. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(5)
  895. }, nil),
  896. },
  897. })
  898. }
  899. func TestV3Handler_sendUserOnline(t *testing.T) {
  900. const onlineUIN = uint32(67890)
  901. sender := newMockPacketSender(t)
  902. svc := newMockLegacyService(t)
  903. recipient := v3Session(testV3UIN)
  904. status := uint32(0x00000010)
  905. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  906. data := v3ServerPacketData(p)
  907. return v3ServerCommand(p) == ICQLegacySrvUserOnline &&
  908. len(data) >= 4 &&
  909. binary.LittleEndian.Uint32(data[0:4]) == onlineUIN
  910. })).Return(nil).Once()
  911. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  912. require.NoError(t, h.sendUserOnline(recipient, onlineUIN, status))
  913. }
  914. func TestV3Handler_sendUserOffline(t *testing.T) {
  915. const offlineUIN = uint32(88888)
  916. sender := newMockPacketSender(t)
  917. svc := newMockLegacyService(t)
  918. recipient := v3Session(testV3UIN)
  919. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  920. data := v3ServerPacketData(p)
  921. return v3ServerCommand(p) == ICQLegacySrvUserOffline &&
  922. len(data) == 4 &&
  923. binary.LittleEndian.Uint32(data[0:4]) == offlineUIN
  924. })).Return(nil).Once()
  925. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  926. require.NoError(t, h.sendUserOffline(recipient, offlineUIN))
  927. }
  928. func TestV3Handler_sendUserStatus(t *testing.T) {
  929. const (
  930. changedUIN = uint32(88888)
  931. status = uint32(0x00010002)
  932. )
  933. sender := newMockPacketSender(t)
  934. svc := newMockLegacyService(t)
  935. recipient := v3Session(testV3UIN)
  936. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  937. data := v3ServerPacketData(p)
  938. return v3ServerCommand(p) == ICQLegacySrvUserStatus &&
  939. len(data) == 8 &&
  940. binary.LittleEndian.Uint32(data[0:4]) == changedUIN &&
  941. binary.LittleEndian.Uint16(data[4:6]) == uint16(status&0xFFFF) &&
  942. binary.LittleEndian.Uint16(data[6:8]) == uint16(status>>16)
  943. })).Return(nil).Once()
  944. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  945. require.NoError(t, h.sendUserStatus(recipient, changedUIN, status))
  946. }
  947. func TestV3Handler_sendOnlineMessage(t *testing.T) {
  948. const fromUIN = uint32(55555)
  949. sender := newMockPacketSender(t)
  950. svc := newMockLegacyService(t)
  951. recipient := v3Session(testV3UIN)
  952. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  953. data := v3ServerPacketData(p)
  954. return v3ServerCommand(p) == ICQLegacySrvSysMsgOnline &&
  955. len(data) >= 8 &&
  956. binary.LittleEndian.Uint32(data[0:4]) == fromUIN
  957. })).Return(nil).Once()
  958. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  959. require.NoError(t, h.sendOnlineMessage(recipient, fromUIN, 0x0001, "hello", testV3Seq2))
  960. }
  961. func TestV3Handler_sendSearchFound(t *testing.T) {
  962. sender := newMockPacketSender(t)
  963. svc := newMockLegacyService(t)
  964. session := v3Session(testV3UIN)
  965. sender.EXPECT().SendToSession(session, mock.MatchedBy(func(p []byte) bool {
  966. data := v3ServerPacketData(p)
  967. return v3ServerCommand(p) == 0x008C &&
  968. len(data) >= 4 &&
  969. binary.LittleEndian.Uint32(data[0:4]) == uint32(99999)
  970. })).Return(nil).Once()
  971. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  972. require.NoError(t, h.sendSearchFound(session, testV3Seq2, 99999, "nick", "first", "last", "a@b.com", 0))
  973. }
  974. func TestV3Handler_sendSearchDone(t *testing.T) {
  975. sender := newMockPacketSender(t)
  976. svc := newMockLegacyService(t)
  977. session := v3Session(testV3UIN)
  978. sender.EXPECT().SendToSession(session, mock.MatchedBy(func(p []byte) bool {
  979. return v3ServerCommand(p) == 0x00A0 && len(p) == 17
  980. })).Return(nil).Once()
  981. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  982. require.NoError(t, h.sendSearchDone(session, testV3Seq2))
  983. }
  984. func TestV3Handler_sendDeptsList1(t *testing.T) {
  985. sender := newMockPacketSender(t)
  986. svc := newMockLegacyService(t)
  987. session := v3Session(testV3UIN)
  988. sender.EXPECT().SendToSession(session, mock.MatchedBy(func(p []byte) bool {
  989. return v3ServerCommand(p) == ICQLegacySrvUserDepsList1
  990. })).Return(nil).Once()
  991. h := newTestV3Handler(t, sender, svc, newTestLegacySessionManager())
  992. require.NoError(t, h.sendDeptsList1(session, testV3Seq2))
  993. }
  994. func TestV3Handler_HandleSetStatus_notifiesContacts(t *testing.T) {
  995. const contactUIN = uint32(88888)
  996. changer := v3Session(testV3UIN)
  997. contact := v3Session(contactUIN)
  998. sender := newMockPacketSender(t)
  999. svc := newMockLegacyService(t)
  1000. sessions := newTestLegacySessionManager()
  1001. registerSession(sessions, changer)
  1002. registerSession(sessions, contact)
  1003. svc.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  1004. Return(&StatusChangeResult{NotifyTargets: []NotifyTarget{{UIN: contactUIN}}}, nil).Once()
  1005. sender.EXPECT().SendPacket(changer.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1006. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1007. return v3ServerCommand(p) == ICQLegacySrvUserStatus
  1008. })).Return(nil).Once()
  1009. h := newTestV3Handler(t, sender, svc, sessions)
  1010. pkt := defaultV3Pkt(ICQLegacyCmdSetStatus, v3SetStatusData(0x00000002))
  1011. require.NoError(t, h.Handle(changer, testV3Addr, pkt))
  1012. }
  1013. func TestV3Handler_HandleLogoff_notifiesContacts(t *testing.T) {
  1014. const contactUIN = uint32(88888)
  1015. loggingOff := v3Session(testV3UIN)
  1016. contact := v3Session(contactUIN)
  1017. contact.SetContactList([]uint32{testV3UIN})
  1018. sender := newMockPacketSender(t)
  1019. svc := newMockLegacyService(t)
  1020. sessions := newTestLegacySessionManager()
  1021. registerSession(sessions, loggingOff)
  1022. registerSession(sessions, contact)
  1023. svc.EXPECT().NotifyUserOffline(mock.Anything, testV3UIN).Return(nil).Once()
  1024. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1025. return v3ServerCommand(p) == ICQLegacySrvUserOffline
  1026. })).Return(nil).Once()
  1027. h := newTestV3HandlerWithDispatcher(t, sender, svc, sessions)
  1028. pkt := defaultV3Pkt(ICQLegacyCmdLogoff, nil)
  1029. require.NoError(t, h.Handle(loggingOff, testV3Addr, pkt))
  1030. }