v5_handler_test.go 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528
  1. package icq_legacy
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "log/slog"
  8. "net"
  9. "strconv"
  10. "testing"
  11. "time"
  12. "github.com/mk6i/open-oscar-server/config"
  13. "github.com/mk6i/open-oscar-server/state"
  14. "github.com/mk6i/open-oscar-server/wire"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/mock"
  17. "github.com/stretchr/testify/require"
  18. )
  19. const (
  20. testV5UIN = uint32(12345)
  21. testV5SessionID = uint32(0xBEEF)
  22. testV5Seq1 = uint16(42)
  23. testV5Seq2 = uint16(7)
  24. )
  25. var testV5Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4000}
  26. // noopSessionRegistry satisfies SessionRegistry for tests that remove legacy sessions.
  27. type noopSessionRegistry struct{}
  28. func (noopSessionRegistry) RemoveSession(*state.Session) {}
  29. func newTestV5SessionManager() *LegacySessionManager {
  30. return NewLegacySessionManager(noopSessionRegistry{}, config.ICQLegacyConfig{}, slog.Default())
  31. }
  32. // newTestLegacySessionManager is shared by V1–V4 handler tests.
  33. func newTestLegacySessionManager() *LegacySessionManager {
  34. return newTestV5SessionManager()
  35. }
  36. func newTestV5Handler(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V5Handler {
  37. t.Helper()
  38. if sessions == nil {
  39. sessions = newTestV5SessionManager()
  40. }
  41. return NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), slog.Default())
  42. }
  43. func buildPlainV5ClientPacket(pkt V5ClientPacket) []byte {
  44. size := 24 + len(pkt.Data)
  45. buf := make([]byte, size)
  46. binary.LittleEndian.PutUint16(buf[0:2], pkt.Version)
  47. binary.LittleEndian.PutUint32(buf[2:6], pkt.Zero)
  48. binary.LittleEndian.PutUint32(buf[6:10], pkt.UIN)
  49. binary.LittleEndian.PutUint32(buf[10:14], pkt.SessionID)
  50. binary.LittleEndian.PutUint16(buf[14:16], pkt.Command)
  51. binary.LittleEndian.PutUint16(buf[16:18], pkt.SeqNum1)
  52. binary.LittleEndian.PutUint16(buf[18:20], pkt.SeqNum2)
  53. if len(pkt.Data) > 0 {
  54. copy(buf[24:], pkt.Data)
  55. }
  56. return buf
  57. }
  58. // buildEncryptedV5ClientPacket builds a wire-format V5 client packet for Handle().
  59. // Handle applies DecryptV5Packet, which is an XOR-based involution, so we pre-apply
  60. // DecryptV5Packet to plaintext to produce bytes that decrypt back to the intended fields.
  61. func buildEncryptedV5ClientPacket(t *testing.T, pkt V5ClientPacket) []byte {
  62. t.Helper()
  63. buf := buildPlainV5ClientPacket(pkt)
  64. // Place a valid checkcode at 0x14 (required for decryption key derivation).
  65. cc := calculateV5CheckCode(buf)
  66. binary.LittleEndian.PutUint32(buf[20:24], cc)
  67. DecryptV5Packet(buf, 0)
  68. // Verify Handle's decrypt path recovers the intended command.
  69. verify := make([]byte, len(buf))
  70. copy(verify, buf)
  71. DecryptV5Packet(verify, 0)
  72. require.Equal(t, pkt.Command, binary.LittleEndian.Uint16(verify[14:16]),
  73. "V5 test packet builder failed for command 0x%04X", pkt.Command)
  74. return buf
  75. }
  76. func v5ServerCommand(packet []byte) uint16 {
  77. if len(packet) < 9 {
  78. return 0
  79. }
  80. return binary.LittleEndian.Uint16(packet[7:9])
  81. }
  82. func v5ServerPacketData(packet []byte) []byte {
  83. if len(packet) <= 21 {
  84. return nil
  85. }
  86. return packet[21:]
  87. }
  88. func v5ServerSeq1(packet []byte) uint16 {
  89. if len(packet) < 11 {
  90. return 0
  91. }
  92. return binary.LittleEndian.Uint16(packet[9:11])
  93. }
  94. func v5ServerSeq2(packet []byte) uint16 {
  95. if len(packet) < 13 {
  96. return 0
  97. }
  98. return binary.LittleEndian.Uint16(packet[11:13])
  99. }
  100. func newTestV5HandlerWithDispatcher(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V5Handler {
  101. t.Helper()
  102. logger := slog.Default()
  103. v1 := NewV1Handler(sessions, svc, sender, logger)
  104. v2 := NewV2Handler(sessions, svc, sender, NewV2PacketBuilder(), logger)
  105. v3 := NewV3Handler(sessions, svc, sender, NewV3PacketBuilder(sessions, nil), logger)
  106. v4 := NewV4Handler(sessions, svc, sender, NewV4PacketBuilder(sessions, nil), logger)
  107. v5 := NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), logger)
  108. cfg := config.ICQLegacyConfig{SupportedVersions: []int{3, 4, 5}}
  109. dispatcher := NewProtocolDispatcher(v1, v2, v3, v4, v5, cfg, logger)
  110. v5.SetDispatcher(dispatcher)
  111. return v5
  112. }
  113. func sessionWithDirectConn(uin uint32, tcpPort, internalIP uint32, dcType uint8) *LegacySession {
  114. s := sessionWithInstance(uin)
  115. s.SetDirectConnectionInfo(tcpPort, internalIP, 5, dcType)
  116. return s
  117. }
  118. func sessionWithInstance(uin uint32) *LegacySession {
  119. s := newTestLegacySession(uin, legacySessionOptOSCARSess, func(ls *LegacySession) {
  120. ls.SessionID = testV5SessionID
  121. ls.Version = ICQLegacyVersionV5
  122. })
  123. return s
  124. }
  125. func expectV5NotConnected(sender *mockPacketSender) {
  126. sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  127. Run(func(_ *net.UDPAddr, p []byte) {
  128. if got := v5ServerCommand(p); got != ICQLegacySrvNotConnected {
  129. panic(fmt.Sprintf("got command 0x%04X, want NOT_CONNECTED", got))
  130. }
  131. }).Return(nil).Once()
  132. }
  133. func registerSession(mgr *LegacySessionManager, session *LegacySession) {
  134. mgr.mu.Lock()
  135. defer mgr.mu.Unlock()
  136. mgr.sessions[session.UIN] = session
  137. if session.Addr != nil {
  138. mgr.addrIndex[session.Addr.String()] = session
  139. }
  140. }
  141. func defaultV5Pkt(command uint16, data []byte) V5ClientPacket {
  142. return V5ClientPacket{
  143. Version: ICQLegacyVersionV5,
  144. UIN: testV5UIN,
  145. SessionID: testV5SessionID,
  146. Command: command,
  147. SeqNum1: testV5Seq1,
  148. SeqNum2: testV5Seq2,
  149. Data: data,
  150. }
  151. }
  152. func v5LPString(s string) []byte {
  153. buf := new(bytes.Buffer)
  154. writeLegacyString(buf, s)
  155. return buf.Bytes()
  156. }
  157. func v5GetDepsData(uin uint32, password string) []byte {
  158. pwd := []byte(password)
  159. buf := make([]byte, 4+2+len(pwd))
  160. binary.LittleEndian.PutUint32(buf[0:4], uin)
  161. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
  162. copy(buf[6:], pwd)
  163. return buf
  164. }
  165. func v5LoginData(password string, minimal bool) []byte {
  166. pwd := []byte(password)
  167. if minimal {
  168. buf := make([]byte, 4+4+2+len(pwd))
  169. binary.LittleEndian.PutUint32(buf[4:8], 4000)
  170. binary.LittleEndian.PutUint16(buf[8:10], uint16(len(pwd)))
  171. copy(buf[10:], pwd)
  172. return buf
  173. }
  174. buf := make([]byte, 4+4+2+len(pwd)+4+4+1+4+4+28)
  175. off := 0
  176. off += 4 // time
  177. binary.LittleEndian.PutUint32(buf[off:], 4000)
  178. off += 4
  179. binary.LittleEndian.PutUint16(buf[off:], uint16(len(pwd)))
  180. off += 2
  181. copy(buf[off:], pwd)
  182. off += len(pwd)
  183. binary.LittleEndian.PutUint32(buf[off:], 0x98)
  184. off += 4
  185. binary.LittleEndian.PutUint32(buf[off:], 0x7F000001)
  186. off += 4
  187. buf[off] = 0
  188. off++
  189. binary.LittleEndian.PutUint32(buf[off:], 0)
  190. off += 4
  191. binary.LittleEndian.PutUint32(buf[off:], 0)
  192. return buf
  193. }
  194. func v5ContactListData(uins []uint32) []byte {
  195. buf := make([]byte, 1+4*len(uins))
  196. buf[0] = byte(len(uins))
  197. for i, u := range uins {
  198. binary.LittleEndian.PutUint32(buf[1+4*i:], u)
  199. }
  200. return buf
  201. }
  202. func v5MessageData(toUIN uint32, msgType uint16, message string) []byte {
  203. msg := []byte(message)
  204. buf := make([]byte, 4+2+2+len(msg))
  205. binary.LittleEndian.PutUint32(buf[0:4], toUIN)
  206. binary.LittleEndian.PutUint16(buf[4:6], msgType)
  207. binary.LittleEndian.PutUint16(buf[6:8], uint16(len(msg)))
  208. copy(buf[8:], msg)
  209. return buf
  210. }
  211. func v5MetaUserData(subCmd uint16, subData []byte) []byte {
  212. buf := make([]byte, 2+len(subData))
  213. binary.LittleEndian.PutUint16(buf[0:2], subCmd)
  214. copy(buf[2:], subData)
  215. return buf
  216. }
  217. func metaUserPkt(subCmd uint16, subData []byte) V5ClientPacket {
  218. return defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(subCmd, subData))
  219. }
  220. func v5MetaWireMarshal(t *testing.T, body any) []byte {
  221. t.Helper()
  222. var buf bytes.Buffer
  223. require.NoError(t, wire.MarshalLE(body, &buf))
  224. return buf.Bytes()
  225. }
  226. func v5MetaSetBasicData() []byte {
  227. buf := new(bytes.Buffer)
  228. for _, s := range []string{
  229. "nick", "first", "last", "a@b.com", "", "", "city", "ST",
  230. "phone", "fax", "addr", "cell", "zip",
  231. } {
  232. writeLegacyString(buf, s)
  233. }
  234. tail := []byte{0x01, 0x00, 0, 1} // country=1, gmt=0, publish=1
  235. return append(buf.Bytes(), tail...)
  236. }
  237. func v5MetaUnregisterData(uin uint32, password string) []byte {
  238. pwd := []byte(password)
  239. buf := make([]byte, 4+2+len(pwd))
  240. binary.LittleEndian.PutUint32(buf[0:4], uin)
  241. binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
  242. copy(buf[6:], pwd)
  243. return buf
  244. }
  245. func v5MetaSetPassData(password string) []byte {
  246. pwd := []byte(password)
  247. buf := make([]byte, 2+len(pwd))
  248. binary.LittleEndian.PutUint16(buf[0:2], uint16(len(pwd)))
  249. copy(buf[2:], pwd)
  250. return buf
  251. }
  252. func v5MetaSetHPCatData(enabled bool, index uint16, desc string) []byte {
  253. d := []byte(desc)
  254. buf := make([]byte, 5+len(d))
  255. if enabled {
  256. buf[0] = 1
  257. }
  258. binary.LittleEndian.PutUint16(buf[1:3], index)
  259. binary.LittleEndian.PutUint16(buf[3:5], uint16(len(d)))
  260. copy(buf[5:], d)
  261. return buf
  262. }
  263. func v5MetaSearchNameData() []byte {
  264. return append(append(v5LPString("nick"), v5LPString("first")...), v5LPString("last")...)
  265. }
  266. func v5MetaSearchEmailData(email string) []byte {
  267. return v5LPString(email)
  268. }
  269. func v5MetaInterestsWireData(t *testing.T) []byte {
  270. t.Helper()
  271. return v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{
  272. Interests: []struct {
  273. Code uint16
  274. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  275. }{
  276. {Code: 1, Keyword: "kw1"},
  277. {Code: 2, Keyword: "kw2"},
  278. {Code: 3, Keyword: "kw3"},
  279. {Code: 4, Keyword: "kw4"},
  280. },
  281. })
  282. }
  283. func v5MetaAffiliationsWireData(t *testing.T) []byte {
  284. t.Helper()
  285. return v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
  286. PastAffiliations: []struct {
  287. Code uint16
  288. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  289. }{
  290. {Code: 1, Keyword: "p1"},
  291. {Code: 2, Keyword: "p2"},
  292. {Code: 3, Keyword: "p3"},
  293. },
  294. Affiliations: []struct {
  295. Code uint16
  296. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  297. }{
  298. {Code: 4, Keyword: "c1"},
  299. {Code: 5, Keyword: "c2"},
  300. {Code: 6, Keyword: "c3"},
  301. },
  302. })
  303. }
  304. func v5TargetUINData(target uint32) []byte {
  305. buf := make([]byte, 4)
  306. binary.LittleEndian.PutUint32(buf, target)
  307. return buf
  308. }
  309. func testAuthSuccessInstance() *state.SessionInstance {
  310. return newTestOSCARInstance(state.DisplayScreenName(strconv.FormatUint(uint64(testV5UIN), 10)))
  311. }
  312. func testMinimalUser() *state.User {
  313. u := &state.User{}
  314. u.ICQInfo.Basic.Nickname = "nick"
  315. u.ICQInfo.Basic.FirstName = "first"
  316. u.ICQInfo.Basic.LastName = "last"
  317. return u
  318. }
  319. // v5HandleEnv groups per-case dependencies for runV5HandleCases.
  320. type v5HandleEnv struct {
  321. T *testing.T
  322. Sender *mockPacketSender
  323. Service *mockLegacyService
  324. Sessions *LegacySessionManager
  325. Session *LegacySession // same pointer passed to Handle
  326. }
  327. // v5HandleCase is one row in a table-driven V5Handler.Handle test.
  328. type v5HandleCase struct {
  329. name string
  330. session *LegacySession
  331. pkt V5ClientPacket
  332. // packet, when set, supplies wire bytes instead of encrypting pkt.
  333. packet func(t *testing.T) []byte
  334. // setup configures mocks and session manager before Handle; the returned
  335. // function validates the Handle result (and optional post-conditions).
  336. setup func(v5HandleEnv) func(error)
  337. }
  338. // v5CaseSetup runs pre-Handle wiring and returns the post-Handle check.
  339. // If after is nil, Handle is expected to return nil.
  340. func v5CaseSetup(pre func(v5HandleEnv), after func(e v5HandleEnv, err error)) func(v5HandleEnv) func(error) {
  341. return func(e v5HandleEnv) func(error) {
  342. if pre != nil {
  343. pre(e)
  344. }
  345. return func(err error) {
  346. if after != nil {
  347. after(e, err)
  348. return
  349. }
  350. assert.NoError(e.T, err)
  351. }
  352. }
  353. }
  354. func runV5HandleCases(t *testing.T, cases []v5HandleCase) {
  355. t.Helper()
  356. for _, tt := range cases {
  357. t.Run(tt.name, func(t *testing.T) {
  358. sender := newMockPacketSender(t)
  359. svc := newMockLegacyService(t)
  360. sessions := newTestV5SessionManager()
  361. e := v5HandleEnv{
  362. T: t,
  363. Sender: sender,
  364. Service: svc,
  365. Sessions: sessions,
  366. Session: tt.session,
  367. }
  368. var check func(error)
  369. if tt.setup != nil {
  370. check = tt.setup(e)
  371. } else {
  372. check = func(err error) { assert.NoError(t, err) }
  373. }
  374. h := newTestV5Handler(t, sender, svc, sessions)
  375. var wire []byte
  376. if tt.packet != nil {
  377. wire = tt.packet(t)
  378. } else {
  379. wire = buildEncryptedV5ClientPacket(t, tt.pkt)
  380. }
  381. check(h.Handle(tt.session, testV5Addr, wire))
  382. })
  383. }
  384. }
  385. func TestV5Handler_Handle(t *testing.T) {
  386. runV5HandleCases(t, []v5HandleCase{
  387. {
  388. name: "packet too short",
  389. packet: func(_ *testing.T) []byte { return []byte{0x01, 0x02, 0x03} },
  390. setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
  391. require.Error(e.T, err)
  392. assert.Contains(e.T, err.Error(), "V5 packet too short")
  393. }),
  394. },
  395. {
  396. name: "nil session disallowed command",
  397. session: nil,
  398. pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
  399. setup: v5CaseSetup(func(e v5HandleEnv) {
  400. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  401. Run(func(_ *net.UDPAddr, p []byte) {
  402. assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
  403. }).Return(nil).Once()
  404. }, nil),
  405. },
  406. {
  407. name: "nil session ACK allowed",
  408. session: nil,
  409. pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
  410. },
  411. {
  412. name: "session state updated",
  413. session: sessionWithInstance(testV5UIN),
  414. pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
  415. setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
  416. assert.NoError(e.T, err)
  417. assert.Equal(t, testV5Seq1, e.Session.SeqNumClient)
  418. assert.WithinDuration(e.T, time.Now(), e.Session.GetLastActivity(), time.Second)
  419. }),
  420. },
  421. {
  422. name: "unknown command",
  423. session: sessionWithInstance(testV5UIN),
  424. pkt: defaultV5Pkt(0xFFFF, nil),
  425. setup: v5CaseSetup(func(e v5HandleEnv) {
  426. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  427. Run(func(_ *LegacySession, p []byte) {
  428. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  429. }).Return(nil).Once()
  430. }, nil),
  431. },
  432. {
  433. name: "inline ACK command",
  434. session: sessionWithInstance(testV5UIN),
  435. pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
  436. },
  437. })
  438. }
  439. func TestV5Handler_HandleFirstLogin(t *testing.T) {
  440. runV5HandleCases(t, []v5HandleCase{
  441. {
  442. name: "with session_id2 in data",
  443. session: nil,
  444. pkt: defaultV5Pkt(ICQLegacyCmdFirstLogin, func() []byte {
  445. b := make([]byte, 4)
  446. binary.LittleEndian.PutUint32(b, 0xCAFEBABE)
  447. return b
  448. }()),
  449. setup: v5CaseSetup(
  450. func(e v5HandleEnv) {
  451. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  452. Run(func(_ *net.UDPAddr, p []byte) {
  453. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  454. assert.GreaterOrEqual(t, len(p), 28)
  455. }).Return(nil).Once()
  456. },
  457. nil,
  458. ),
  459. },
  460. {
  461. name: "empty data",
  462. session: nil,
  463. pkt: defaultV5Pkt(ICQLegacyCmdFirstLogin, nil),
  464. setup: v5CaseSetup(
  465. func(e v5HandleEnv) {
  466. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  467. Run(func(_ *net.UDPAddr, p []byte) {
  468. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  469. }).Return(nil).Once()
  470. },
  471. nil,
  472. ),
  473. },
  474. })
  475. }
  476. func TestV5Handler_HandleGetDeps(t *testing.T) {
  477. runV5HandleCases(t, []v5HandleCase{
  478. {
  479. name: "valid credentials",
  480. session: nil,
  481. pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "secret")),
  482. setup: v5CaseSetup(
  483. func(e v5HandleEnv) {
  484. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "secret").Return(true, nil).Once()
  485. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  486. },
  487. nil,
  488. ),
  489. },
  490. {
  491. name: "invalid credentials",
  492. session: nil,
  493. pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "wrong")),
  494. setup: v5CaseSetup(
  495. func(e v5HandleEnv) {
  496. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "wrong").Return(false, nil).Once()
  497. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  498. Run(func(_ *net.UDPAddr, p []byte) {
  499. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  500. }).Return(nil).Once()
  501. },
  502. nil,
  503. ),
  504. },
  505. {
  506. name: "service error",
  507. session: nil,
  508. pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "secret")),
  509. setup: v5CaseSetup(
  510. func(e v5HandleEnv) {
  511. e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "secret").
  512. Return(false, errors.New("db down")).Once()
  513. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  514. Run(func(_ *net.UDPAddr, p []byte) {
  515. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  516. }).Return(nil).Once()
  517. },
  518. nil,
  519. ),
  520. },
  521. {
  522. name: "packet too short",
  523. session: nil,
  524. pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, []byte{0x01}),
  525. setup: v5CaseSetup(
  526. func(e v5HandleEnv) {
  527. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  528. Run(func(_ *net.UDPAddr, p []byte) {
  529. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  530. }).Return(nil).Once()
  531. },
  532. nil,
  533. ),
  534. },
  535. })
  536. }
  537. func TestV5Handler_HandleLogin(t *testing.T) {
  538. runV5HandleCases(t, []v5HandleCase{
  539. {
  540. name: "auth success",
  541. session: nil,
  542. pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("secret", true)),
  543. setup: v5CaseSetup(
  544. func(e v5HandleEnv) {
  545. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
  546. return req.UIN == testV5UIN && req.Password == "secret"
  547. })).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
  548. e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  549. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  550. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
  551. Return(nil).Once()
  552. },
  553. func(e v5HandleEnv, err error) {
  554. assert.NoError(e.T, err)
  555. assert.NotNil(t, e.Sessions.GetSession(testV5UIN))
  556. },
  557. ),
  558. },
  559. {
  560. name: "bad password",
  561. session: nil,
  562. pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("wrong", true)),
  563. setup: v5CaseSetup(
  564. func(e v5HandleEnv) {
  565. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
  566. Return(&AuthResult{Success: false, ErrorCode: 0x0001}, nil).Once()
  567. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  568. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  569. Run(func(_ *net.UDPAddr, p []byte) {
  570. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  571. }).Return(nil).Once()
  572. },
  573. nil,
  574. ),
  575. },
  576. {
  577. name: "auth service error",
  578. session: nil,
  579. pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("secret", true)),
  580. setup: v5CaseSetup(
  581. func(e v5HandleEnv) {
  582. e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
  583. Return(nil, errors.New("auth failed")).Once()
  584. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  585. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  586. Run(func(_ *net.UDPAddr, p []byte) {
  587. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  588. }).Return(nil).Once()
  589. },
  590. nil,
  591. ),
  592. },
  593. {
  594. name: "packet too short",
  595. session: nil,
  596. pkt: defaultV5Pkt(ICQLegacyCmdLogin, []byte{0, 0, 0}),
  597. setup: v5CaseSetup(
  598. func(e v5HandleEnv) {
  599. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once() // login ACK
  600. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  601. Run(func(_ *net.UDPAddr, p []byte) {
  602. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  603. }).Return(nil).Once()
  604. },
  605. nil,
  606. ),
  607. },
  608. {
  609. name: "truncated password field",
  610. session: nil,
  611. pkt: defaultV5Pkt(ICQLegacyCmdLogin, func() []byte {
  612. buf := make([]byte, 12)
  613. binary.LittleEndian.PutUint32(buf[4:8], 4000)
  614. binary.LittleEndian.PutUint16(buf[8:10], 20) // claims 20 bytes but only 2 available
  615. return buf
  616. }()),
  617. setup: v5CaseSetup(
  618. func(e v5HandleEnv) {
  619. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  620. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  621. Run(func(_ *net.UDPAddr, p []byte) {
  622. assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
  623. }).Return(nil).Once()
  624. },
  625. nil,
  626. ),
  627. },
  628. })
  629. }
  630. func TestV5Handler_HandleContactList(t *testing.T) {
  631. runV5HandleCases(t, []v5HandleCase{
  632. {
  633. name: "valid contact list",
  634. session: sessionWithInstance(testV5UIN),
  635. pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData([]uint32{99999})),
  636. setup: v5CaseSetup(
  637. func(e v5HandleEnv) {
  638. e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
  639. Return(&ContactListResult{}, nil).Once()
  640. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  641. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  642. Run(func(_ *LegacySession, p []byte) {
  643. assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
  644. }).Return(nil).Once()
  645. },
  646. nil,
  647. ),
  648. },
  649. {
  650. name: "nil session",
  651. pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData(nil)),
  652. setup: v5CaseSetup(
  653. func(e v5HandleEnv) {
  654. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  655. Run(func(_ *net.UDPAddr, p []byte) {
  656. assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
  657. }).Return(nil).Once()
  658. },
  659. nil,
  660. ),
  661. },
  662. {
  663. name: "parse error empty data",
  664. session: sessionWithInstance(testV5UIN),
  665. pkt: defaultV5Pkt(ICQLegacyCmdContactList, nil),
  666. setup: v5CaseSetup(
  667. func(e v5HandleEnv) {
  668. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  669. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  670. Run(func(_ *LegacySession, p []byte) {
  671. assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
  672. }).Return(nil).Once()
  673. },
  674. nil,
  675. ),
  676. },
  677. {
  678. name: "service error",
  679. session: sessionWithInstance(testV5UIN),
  680. pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData(nil)),
  681. setup: v5CaseSetup(
  682. func(e v5HandleEnv) {
  683. e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
  684. Return(nil, errors.New("fail")).Once()
  685. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  686. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  687. Run(func(_ *LegacySession, p []byte) {
  688. assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
  689. }).Return(nil).Once()
  690. },
  691. nil,
  692. ),
  693. },
  694. })
  695. }
  696. func TestV5Handler_HandlePing(t *testing.T) {
  697. runV5HandleCases(t, []v5HandleCase{
  698. {
  699. name: "keep alive",
  700. session: sessionWithInstance(testV5UIN),
  701. pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
  702. setup: v5CaseSetup(
  703. func(e v5HandleEnv) {
  704. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  705. Run(func(_ *LegacySession, p []byte) {
  706. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  707. }).Return(nil).Once()
  708. },
  709. nil,
  710. ),
  711. },
  712. {
  713. name: "keep alive 2",
  714. session: sessionWithInstance(testV5UIN),
  715. pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive2, nil),
  716. setup: v5CaseSetup(
  717. func(e v5HandleEnv) {
  718. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  719. Run(func(_ *LegacySession, p []byte) {
  720. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  721. }).Return(nil).Once()
  722. },
  723. nil,
  724. ),
  725. },
  726. {
  727. name: "nil session",
  728. pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
  729. setup: v5CaseSetup(
  730. func(e v5HandleEnv) {
  731. e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
  732. Run(func(_ *net.UDPAddr, p []byte) {
  733. assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
  734. }).Return(nil).Once()
  735. },
  736. nil,
  737. ),
  738. },
  739. })
  740. }
  741. func TestV5Handler_HandleLogoff(t *testing.T) {
  742. runV5HandleCases(t, []v5HandleCase{
  743. {
  744. name: "active session",
  745. session: sessionWithInstance(testV5UIN),
  746. pkt: defaultV5Pkt(ICQLegacyCmdLogoff, nil),
  747. setup: v5CaseSetup(
  748. func(e v5HandleEnv) {
  749. registerSession(e.Sessions, e.Session)
  750. e.Service.EXPECT().NotifyUserOffline(mock.Anything, testV5UIN).Return(nil).Once()
  751. },
  752. func(e v5HandleEnv, err error) {
  753. assert.NoError(e.T, err)
  754. assert.Nil(t, e.Sessions.GetSession(testV5UIN))
  755. },
  756. ),
  757. },
  758. {
  759. name: "nil session",
  760. pkt: defaultV5Pkt(ICQLegacyCmdLogoff, nil),
  761. setup: v5CaseSetup(
  762. func(e v5HandleEnv) {
  763. expectV5NotConnected(e.Sender)
  764. },
  765. nil,
  766. ),
  767. },
  768. })
  769. }
  770. func TestV5Handler_HandleSetStatus(t *testing.T) {
  771. runV5HandleCases(t, []v5HandleCase{
  772. {
  773. name: "valid status change",
  774. session: sessionWithInstance(testV5UIN),
  775. pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
  776. b := make([]byte, 4)
  777. binary.LittleEndian.PutUint32(b, 0x00000001)
  778. return b
  779. }()),
  780. setup: v5CaseSetup(
  781. func(e v5HandleEnv) {
  782. e.Service.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  783. Return(&StatusChangeResult{}, nil).Once()
  784. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  785. },
  786. func(e v5HandleEnv, err error) {
  787. assert.NoError(e.T, err)
  788. assert.Equal(t, uint32(0x00000001), e.Session.GetStatus())
  789. },
  790. ),
  791. },
  792. {
  793. name: "nil session",
  794. pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
  795. b := make([]byte, 4)
  796. binary.LittleEndian.PutUint32(b, 1)
  797. return b
  798. }()),
  799. setup: v5CaseSetup(
  800. func(e v5HandleEnv) {
  801. expectV5NotConnected(e.Sender)
  802. },
  803. nil,
  804. ),
  805. },
  806. {
  807. name: "data too short",
  808. session: sessionWithInstance(testV5UIN),
  809. pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, nil),
  810. setup: v5CaseSetup(
  811. func(e v5HandleEnv) {
  812. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  813. },
  814. nil,
  815. ),
  816. },
  817. })
  818. }
  819. func TestV5Handler_HandleMessage(t *testing.T) {
  820. target := sessionWithInstance(99999)
  821. target.Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4001}
  822. runV5HandleCases(t, []v5HandleCase{
  823. {
  824. name: "online target",
  825. session: sessionWithInstance(testV5UIN),
  826. pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
  827. setup: v5CaseSetup(
  828. func(e v5HandleEnv) {
  829. registerSession(e.Sessions, target)
  830. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  831. Return(&MessageResult{TargetOnline: true}, nil).Once()
  832. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  833. e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
  834. Return(nil).Once()
  835. },
  836. nil,
  837. ),
  838. },
  839. {
  840. name: "offline stored",
  841. session: sessionWithInstance(testV5UIN),
  842. pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
  843. setup: v5CaseSetup(
  844. func(e v5HandleEnv) {
  845. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  846. Return(&MessageResult{StoredOffline: true}, nil).Once()
  847. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  848. },
  849. nil,
  850. ),
  851. },
  852. {
  853. name: "nil session",
  854. pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
  855. setup: v5CaseSetup(
  856. func(e v5HandleEnv) {
  857. expectV5NotConnected(e.Sender)
  858. },
  859. nil,
  860. ),
  861. },
  862. {
  863. name: "parse error",
  864. session: sessionWithInstance(testV5UIN),
  865. pkt: defaultV5Pkt(ICQLegacyCmdThruServer, []byte{1, 2, 3}),
  866. setup: v5CaseSetup(
  867. func(e v5HandleEnv) {
  868. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  869. },
  870. nil,
  871. ),
  872. },
  873. {
  874. name: "service error",
  875. session: sessionWithInstance(testV5UIN),
  876. pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
  877. setup: v5CaseSetup(
  878. func(e v5HandleEnv) {
  879. e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
  880. Return(nil, errors.New("fail")).Once()
  881. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  882. },
  883. nil,
  884. ),
  885. },
  886. })
  887. }
  888. func TestV5Handler_HandleUserAdd(t *testing.T) {
  889. runV5HandleCases(t, []v5HandleCase{
  890. {
  891. name: "success",
  892. session: sessionWithInstance(testV5UIN),
  893. pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
  894. setup: v5CaseSetup(
  895. func(e v5HandleEnv) {
  896. e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  897. Return(&UserAddResult{}, nil).Once()
  898. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  899. },
  900. func(e v5HandleEnv, err error) {
  901. assert.NoError(e.T, err)
  902. assert.Contains(t, e.Session.GetContactList(), uint32(99999))
  903. },
  904. ),
  905. },
  906. {
  907. name: "nil session",
  908. pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
  909. setup: v5CaseSetup(
  910. func(e v5HandleEnv) {
  911. expectV5NotConnected(e.Sender)
  912. },
  913. nil,
  914. ),
  915. },
  916. {
  917. name: "parse error",
  918. session: sessionWithInstance(testV5UIN),
  919. pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, []byte{1, 2}),
  920. setup: v5CaseSetup(
  921. func(e v5HandleEnv) {
  922. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  923. },
  924. nil,
  925. ),
  926. },
  927. {
  928. name: "service error",
  929. session: sessionWithInstance(testV5UIN),
  930. pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
  931. setup: v5CaseSetup(
  932. func(e v5HandleEnv) {
  933. e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  934. Return(nil, errors.New("fail")).Once()
  935. e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  936. },
  937. nil,
  938. ),
  939. },
  940. })
  941. }
  942. func TestV5Handler_sendV5UserOnline(t *testing.T) {
  943. const (
  944. onlineUIN = uint32(67890)
  945. tcpPort = uint32(5190)
  946. internalIP = uint32(0x7F000001)
  947. dcType = uint8(2)
  948. userStatus = uint32(0x00000010)
  949. )
  950. t.Run("V5 peer includes connection info", func(t *testing.T) {
  951. sender := newMockPacketSender(t)
  952. svc := newMockLegacyService(t)
  953. sessions := newTestV5SessionManager()
  954. onlineSess := sessionWithDirectConn(onlineUIN, tcpPort, internalIP, dcType)
  955. registerSession(sessions, onlineSess)
  956. recipient := sessionWithInstance(testV5UIN)
  957. wantExtIP := onlineSess.GetExternalIP()
  958. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  959. if v5ServerCommand(p) != ICQLegacySrvUserOnline {
  960. return false
  961. }
  962. data := v5ServerPacketData(p)
  963. return len(data) >= 17 &&
  964. binary.LittleEndian.Uint32(data[0:4]) == onlineUIN &&
  965. binary.LittleEndian.Uint32(data[4:8]) == wantExtIP &&
  966. binary.LittleEndian.Uint32(data[8:12]) == tcpPort &&
  967. binary.LittleEndian.Uint32(data[12:16]) == internalIP &&
  968. data[16] == dcType
  969. })).Return(nil).Once()
  970. h := newTestV5Handler(t, sender, svc, sessions)
  971. assert.NoError(t, h.sendV5UserOnline(recipient, onlineUIN, userStatus))
  972. })
  973. t.Run("non-V5 online user zeros connection info", func(t *testing.T) {
  974. sender := newMockPacketSender(t)
  975. svc := newMockLegacyService(t)
  976. sessions := newTestV5SessionManager()
  977. onlineSess := newTestLegacySession(onlineUIN, legacySessionOptVersion(ICQLegacyVersionV3))
  978. onlineSess.SetDirectConnectionInfo(tcpPort, internalIP, 5, dcType)
  979. registerSession(sessions, onlineSess)
  980. recipient := sessionWithInstance(testV5UIN)
  981. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  982. data := v5ServerPacketData(p)
  983. return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
  984. len(data) >= 17 &&
  985. binary.LittleEndian.Uint32(data[4:8]) == 0 &&
  986. binary.LittleEndian.Uint32(data[8:12]) == 0
  987. })).Return(nil).Once()
  988. h := newTestV5Handler(t, sender, svc, sessions)
  989. assert.NoError(t, h.sendV5UserOnline(recipient, onlineUIN, userStatus))
  990. })
  991. }
  992. func TestV5Handler_sendV5AckToAddr(t *testing.T) {
  993. sender := newMockPacketSender(t)
  994. svc := newMockLegacyService(t)
  995. h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
  996. sender.EXPECT().SendPacket(testV5Addr, mock.MatchedBy(func(p []byte) bool {
  997. return v5ServerCommand(p) == ICQLegacySrvAck &&
  998. v5ServerSeq1(p) == testV5Seq1 &&
  999. v5ServerSeq2(p) == testV5Seq2 &&
  1000. binary.LittleEndian.Uint32(p[3:7]) == testV5SessionID &&
  1001. binary.LittleEndian.Uint32(p[13:17]) == testV5UIN &&
  1002. len(v5ServerPacketData(p)) == 0
  1003. })).Return(nil).Once()
  1004. assert.NoError(t, h.sendV5AckToAddr(testV5Addr, testV5SessionID, testV5UIN, testV5Seq1, testV5Seq2))
  1005. }
  1006. func TestV5Handler_sendV5UserOffline(t *testing.T) {
  1007. const offlineUIN = uint32(88888)
  1008. sender := newMockPacketSender(t)
  1009. svc := newMockLegacyService(t)
  1010. recipient := sessionWithInstance(testV5UIN)
  1011. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  1012. data := v5ServerPacketData(p)
  1013. return v5ServerCommand(p) == ICQLegacySrvUserOffline &&
  1014. len(data) == 4 &&
  1015. binary.LittleEndian.Uint32(data[0:4]) == offlineUIN
  1016. })).Return(nil).Once()
  1017. h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
  1018. assert.NoError(t, h.sendV5UserOffline(recipient, offlineUIN))
  1019. }
  1020. func TestV5Handler_sendV5UserStatus(t *testing.T) {
  1021. const (
  1022. changedUIN = uint32(88888)
  1023. status = uint32(0x00010002) // estat=1, status=2
  1024. )
  1025. sender := newMockPacketSender(t)
  1026. svc := newMockLegacyService(t)
  1027. recipient := sessionWithInstance(testV5UIN)
  1028. sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
  1029. data := v5ServerPacketData(p)
  1030. return v5ServerCommand(p) == ICQLegacySrvUserStatus &&
  1031. len(data) == 8 &&
  1032. binary.LittleEndian.Uint32(data[0:4]) == changedUIN &&
  1033. binary.LittleEndian.Uint16(data[4:6]) == uint16(status&0xFFFF) &&
  1034. binary.LittleEndian.Uint16(data[6:8]) == uint16(status>>16)
  1035. })).Return(nil).Once()
  1036. h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
  1037. assert.NoError(t, h.sendV5UserStatus(recipient, changedUIN, status))
  1038. }
  1039. func TestV5Handler_HandleLogoff_notifiesContacts(t *testing.T) {
  1040. const contactUIN = uint32(88888)
  1041. sender := newMockPacketSender(t)
  1042. svc := newMockLegacyService(t)
  1043. sessions := newTestV5SessionManager()
  1044. loggingOff := sessionWithInstance(testV5UIN)
  1045. loggingOff.SetContactList([]uint32{contactUIN})
  1046. contact := sessionWithInstance(contactUIN)
  1047. contact.SetContactList([]uint32{testV5UIN})
  1048. registerSession(sessions, loggingOff)
  1049. registerSession(sessions, contact)
  1050. svc.EXPECT().NotifyUserOffline(mock.Anything, testV5UIN).Return(nil).Once()
  1051. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1052. data := v5ServerPacketData(p)
  1053. return v5ServerCommand(p) == ICQLegacySrvUserOffline &&
  1054. len(data) == 4 &&
  1055. binary.LittleEndian.Uint32(data[0:4]) == testV5UIN
  1056. })).Return(nil).Once()
  1057. h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
  1058. pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdLogoff, nil))
  1059. assert.NoError(t, h.Handle(loggingOff, testV5Addr, pkt))
  1060. assert.Nil(t, sessions.GetSession(testV5UIN))
  1061. }
  1062. func TestV5Handler_HandleSetStatus_notifiesContacts(t *testing.T) {
  1063. const (
  1064. contactUIN = uint32(88888)
  1065. newStatus = uint32(0x00010002)
  1066. )
  1067. sender := newMockPacketSender(t)
  1068. svc := newMockLegacyService(t)
  1069. sessions := newTestV5SessionManager()
  1070. changer := sessionWithInstance(testV5UIN)
  1071. contact := sessionWithInstance(contactUIN)
  1072. registerSession(sessions, changer)
  1073. registerSession(sessions, contact)
  1074. svc.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
  1075. Return(&StatusChangeResult{
  1076. NotifyTargets: []NotifyTarget{{UIN: contactUIN}},
  1077. }, nil).Once()
  1078. sender.EXPECT().SendPacket(changer.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1079. sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
  1080. data := v5ServerPacketData(p)
  1081. return v5ServerCommand(p) == ICQLegacySrvUserStatus &&
  1082. len(data) == 8 &&
  1083. binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
  1084. binary.LittleEndian.Uint16(data[4:6]) == uint16(newStatus&0xFFFF) &&
  1085. binary.LittleEndian.Uint16(data[6:8]) == uint16(newStatus>>16)
  1086. })).Return(nil).Once()
  1087. h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
  1088. pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
  1089. b := make([]byte, 4)
  1090. binary.LittleEndian.PutUint32(b, newStatus)
  1091. return b
  1092. }()))
  1093. assert.NoError(t, h.Handle(changer, testV5Addr, pkt))
  1094. assert.Equal(t, newStatus, changer.GetStatus())
  1095. }
  1096. func TestV5Handler_HandleUserAdd_notifyTarget(t *testing.T) {
  1097. const targetUIN = uint32(99999)
  1098. userAddResult := &UserAddResult{
  1099. TargetOnline: true,
  1100. TargetStatus: 0x00000010,
  1101. SendYouWereAdded: true,
  1102. }
  1103. t.Run("without dispatcher uses packet builder", func(t *testing.T) {
  1104. sender := newMockPacketSender(t)
  1105. svc := newMockLegacyService(t)
  1106. sessions := newTestV5SessionManager()
  1107. adder := sessionWithDirectConn(testV5UIN, 5191, 0x7F000002, 1)
  1108. adder.SetStatus(0x00000020)
  1109. target := sessionWithInstance(targetUIN)
  1110. registerSession(sessions, adder)
  1111. registerSession(sessions, target)
  1112. svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  1113. Return(userAddResult, nil).Once()
  1114. sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1115. sender.EXPECT().SendToSession(adder, mock.MatchedBy(func(p []byte) bool {
  1116. return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
  1117. len(v5ServerPacketData(p)) >= 4 &&
  1118. binary.LittleEndian.Uint32(v5ServerPacketData(p)[0:4]) == targetUIN
  1119. })).Return(nil).Once()
  1120. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1121. if v5ServerCommand(p) != ICQLegacySrvSysMsgOnline {
  1122. return false
  1123. }
  1124. data := v5ServerPacketData(p)
  1125. return len(data) >= 8 &&
  1126. binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
  1127. binary.LittleEndian.Uint16(data[4:6]) == ICQLegacyMsgAdded
  1128. })).Return(nil).Once()
  1129. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1130. data := v5ServerPacketData(p)
  1131. return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
  1132. len(data) >= 4 &&
  1133. binary.LittleEndian.Uint32(data[0:4]) == testV5UIN
  1134. })).Return(nil).Once()
  1135. h := newTestV5Handler(t, sender, svc, sessions)
  1136. pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
  1137. assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
  1138. assert.Contains(t, adder.GetContactList(), targetUIN)
  1139. })
  1140. t.Run("with dispatcher sends user online via sendV5UserOnline", func(t *testing.T) {
  1141. sender := newMockPacketSender(t)
  1142. svc := newMockLegacyService(t)
  1143. sessions := newTestV5SessionManager()
  1144. adder := sessionWithDirectConn(testV5UIN, 5191, 0x7F000002, 1)
  1145. adder.SetStatus(0x00000020)
  1146. target := sessionWithInstance(targetUIN)
  1147. registerSession(sessions, adder)
  1148. registerSession(sessions, target)
  1149. svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  1150. Return(userAddResult, nil).Once()
  1151. sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1152. sender.EXPECT().SendToSession(adder, mock.MatchedBy(func(p []byte) bool {
  1153. return v5ServerCommand(p) == ICQLegacySrvUserOnline
  1154. })).Return(nil).Once()
  1155. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1156. return v5ServerCommand(p) == ICQLegacySrvSysMsgOnline
  1157. })).Return(nil).Once()
  1158. sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
  1159. data := v5ServerPacketData(p)
  1160. return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
  1161. len(data) >= 17 &&
  1162. binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
  1163. binary.LittleEndian.Uint32(data[8:12]) == 5191 &&
  1164. data[16] == 1
  1165. })).Return(nil).Once()
  1166. h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
  1167. pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
  1168. assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
  1169. })
  1170. t.Run("SendYouWereAdded false skips target notifications", func(t *testing.T) {
  1171. sender := newMockPacketSender(t)
  1172. svc := newMockLegacyService(t)
  1173. sessions := newTestV5SessionManager()
  1174. adder := sessionWithInstance(testV5UIN)
  1175. target := sessionWithInstance(targetUIN)
  1176. registerSession(sessions, adder)
  1177. registerSession(sessions, target)
  1178. svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
  1179. Return(&UserAddResult{
  1180. TargetOnline: true,
  1181. TargetStatus: 0x10,
  1182. SendYouWereAdded: false,
  1183. }, nil).Once()
  1184. sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1185. sender.EXPECT().SendToSession(adder, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1186. h := newTestV5Handler(t, sender, svc, sessions)
  1187. pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
  1188. assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
  1189. })
  1190. }
  1191. func TestV5Handler_HandleOfflineMsgReq(t *testing.T) {
  1192. runV5HandleCases(t, []v5HandleCase{
  1193. {
  1194. name: "no messages",
  1195. session: sessionWithInstance(testV5UIN),
  1196. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
  1197. setup: v5CaseSetup(
  1198. func(e v5HandleEnv) {
  1199. e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
  1200. Return([]LegacyOfflineMessage{}, nil).Once()
  1201. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
  1202. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  1203. Run(func(_ *LegacySession, p []byte) {
  1204. assert.Equal(t, ICQLegacySrvSysMsgDone, v5ServerCommand(p))
  1205. }).Return(nil).Once()
  1206. },
  1207. nil,
  1208. ),
  1209. },
  1210. {
  1211. name: "with messages",
  1212. session: sessionWithInstance(testV5UIN),
  1213. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
  1214. setup: v5CaseSetup(
  1215. func(e v5HandleEnv) {
  1216. e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
  1217. Return([]LegacyOfflineMessage{{FromUIN: 99999, ToUIN: testV5UIN, MsgType: 1, Message: "hi"}}, nil).Once()
  1218. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3) // ack + msg + done
  1219. },
  1220. nil,
  1221. ),
  1222. },
  1223. {
  1224. name: "nil session",
  1225. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
  1226. setup: v5CaseSetup(
  1227. func(e v5HandleEnv) {
  1228. expectV5NotConnected(e.Sender)
  1229. },
  1230. nil,
  1231. ),
  1232. },
  1233. {
  1234. name: "service error",
  1235. session: sessionWithInstance(testV5UIN),
  1236. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
  1237. setup: v5CaseSetup(
  1238. func(e v5HandleEnv) {
  1239. e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
  1240. Return(nil, errors.New("fail")).Once()
  1241. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
  1242. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  1243. Run(func(_ *LegacySession, p []byte) {
  1244. assert.Equal(t, ICQLegacySrvSysMsgDone, v5ServerCommand(p))
  1245. }).Return(nil).Once()
  1246. },
  1247. nil,
  1248. ),
  1249. },
  1250. })
  1251. }
  1252. func TestV5Handler_HandleOfflineMsgAck(t *testing.T) {
  1253. runV5HandleCases(t, []v5HandleCase{
  1254. {
  1255. name: "success",
  1256. session: sessionWithInstance(testV5UIN),
  1257. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
  1258. setup: v5CaseSetup(
  1259. func(e v5HandleEnv) {
  1260. e.Service.EXPECT().AckOfflineMessages(mock.Anything, testV5UIN).Return(nil).Once()
  1261. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1262. },
  1263. nil,
  1264. ),
  1265. },
  1266. {
  1267. name: "nil session",
  1268. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
  1269. setup: v5CaseSetup(
  1270. func(e v5HandleEnv) {
  1271. expectV5NotConnected(e.Sender)
  1272. },
  1273. nil,
  1274. ),
  1275. },
  1276. {
  1277. name: "service error",
  1278. session: sessionWithInstance(testV5UIN),
  1279. pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
  1280. setup: v5CaseSetup(
  1281. func(e v5HandleEnv) {
  1282. e.Service.EXPECT().AckOfflineMessages(mock.Anything, testV5UIN).
  1283. Return(errors.New("fail")).Once()
  1284. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1285. },
  1286. nil,
  1287. ),
  1288. },
  1289. })
  1290. }
  1291. func TestV5Handler_HandleMetaUser(t *testing.T) {
  1292. runV5HandleCases(t, []v5HandleCase{
  1293. {
  1294. name: "login info found",
  1295. session: sessionWithInstance(testV5UIN),
  1296. pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
  1297. setup: v5CaseSetup(
  1298. func(e v5HandleEnv) {
  1299. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(testMinimalUser(), nil).Once()
  1300. e.Service.EXPECT().GetUserInfo(mock.Anything, testV5UIN).
  1301. Return(&LegacyUserSearchResult{UIN: testV5UIN}, nil).Once()
  1302. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
  1303. },
  1304. nil,
  1305. ),
  1306. },
  1307. {
  1308. name: "login info not found",
  1309. session: sessionWithInstance(testV5UIN),
  1310. pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
  1311. setup: v5CaseSetup(
  1312. func(e v5HandleEnv) {
  1313. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(nil, nil).Once()
  1314. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2) // meta ack + fail
  1315. },
  1316. nil,
  1317. ),
  1318. },
  1319. {
  1320. name: "packet too short",
  1321. session: sessionWithInstance(testV5UIN),
  1322. pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, []byte{0x01}),
  1323. setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
  1324. require.Error(e.T, err)
  1325. assert.Contains(e.T, err.Error(), "META_USER packet too short")
  1326. }),
  1327. },
  1328. {
  1329. name: "nil session",
  1330. pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
  1331. setup: v5CaseSetup(
  1332. func(e v5HandleEnv) {
  1333. expectV5NotConnected(e.Sender)
  1334. },
  1335. nil,
  1336. ),
  1337. },
  1338. })
  1339. }
  1340. func TestV5Handler_HandleMetaUser_Login(t *testing.T) {
  1341. runV5HandleCases(t, []v5HandleCase{
  1342. {
  1343. name: "meta login reply",
  1344. session: sessionWithInstance(testV5UIN),
  1345. pkt: metaUserPkt(ICQLegacyMetaLogin, nil),
  1346. setup: v5CaseSetup(
  1347. func(e v5HandleEnv) {
  1348. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1349. },
  1350. nil,
  1351. ),
  1352. },
  1353. {
  1354. name: "meta login empty subData",
  1355. session: sessionWithInstance(testV5UIN),
  1356. pkt: metaUserPkt(ICQLegacyMetaLogin, []byte{}),
  1357. setup: v5CaseSetup(
  1358. func(e v5HandleEnv) {
  1359. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1360. },
  1361. nil,
  1362. ),
  1363. },
  1364. })
  1365. }
  1366. func TestV5Handler_HandleMetaUser_GetInfo(t *testing.T) {
  1367. runV5HandleCases(t, []v5HandleCase{
  1368. {
  1369. name: "login info alt opcode",
  1370. session: sessionWithInstance(testV5UIN),
  1371. pkt: metaUserPkt(ICQLegacyMetaLoginInfo2, v5TargetUINData(testV5UIN)),
  1372. setup: v5CaseSetup(
  1373. func(e v5HandleEnv) {
  1374. e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(testMinimalUser(), nil).Once()
  1375. e.Service.EXPECT().GetUserInfo(mock.Anything, testV5UIN).
  1376. Return(&LegacyUserSearchResult{UIN: testV5UIN}, nil).Once()
  1377. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
  1378. },
  1379. nil,
  1380. ),
  1381. },
  1382. {
  1383. name: "user full info found",
  1384. session: sessionWithInstance(testV5UIN),
  1385. pkt: metaUserPkt(ICQLegacyMetaUserFullInfo, v5TargetUINData(99999)),
  1386. setup: v5CaseSetup(
  1387. func(e v5HandleEnv) {
  1388. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
  1389. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  1390. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  1391. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
  1392. },
  1393. nil,
  1394. ),
  1395. },
  1396. {
  1397. name: "user full info data too short",
  1398. session: sessionWithInstance(testV5UIN),
  1399. pkt: metaUserPkt(ICQLegacyMetaUserFullInfo, []byte{0x01, 0x02, 0x03}),
  1400. setup: v5CaseSetup(
  1401. func(e v5HandleEnv) {
  1402. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1403. },
  1404. nil,
  1405. ),
  1406. },
  1407. {
  1408. name: "user full info2 found",
  1409. session: sessionWithInstance(testV5UIN),
  1410. pkt: metaUserPkt(ICQLegacyMetaUserFullInfo2, v5TargetUINData(99999)),
  1411. setup: v5CaseSetup(
  1412. func(e v5HandleEnv) {
  1413. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
  1414. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  1415. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  1416. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
  1417. },
  1418. nil,
  1419. ),
  1420. },
  1421. {
  1422. name: "user full info2 not found",
  1423. session: sessionWithInstance(testV5UIN),
  1424. pkt: metaUserPkt(ICQLegacyMetaUserFullInfo2, v5TargetUINData(99999)),
  1425. setup: v5CaseSetup(
  1426. func(e v5HandleEnv) {
  1427. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  1428. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1429. },
  1430. nil,
  1431. ),
  1432. },
  1433. {
  1434. name: "user info found",
  1435. session: sessionWithInstance(testV5UIN),
  1436. pkt: metaUserPkt(ICQLegacyMetaUserInfo, v5TargetUINData(99999)),
  1437. setup: v5CaseSetup(
  1438. func(e v5HandleEnv) {
  1439. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  1440. Return(&LegacyUserSearchResult{UIN: 99999, Nickname: "nick"}, nil).Once()
  1441. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1442. },
  1443. nil,
  1444. ),
  1445. },
  1446. {
  1447. name: "user info not found",
  1448. session: sessionWithInstance(testV5UIN),
  1449. pkt: metaUserPkt(ICQLegacyMetaUserInfo, v5TargetUINData(99999)),
  1450. setup: v5CaseSetup(
  1451. func(e v5HandleEnv) {
  1452. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  1453. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1454. },
  1455. nil,
  1456. ),
  1457. },
  1458. })
  1459. }
  1460. func TestV5Handler_HandleMetaUser_SetProfile(t *testing.T) {
  1461. runV5HandleCases(t, []v5HandleCase{
  1462. {
  1463. name: "set basic success",
  1464. session: sessionWithInstance(testV5UIN),
  1465. pkt: metaUserPkt(ICQLegacyMetaSetBasic2, v5MetaSetBasicData()),
  1466. setup: v5CaseSetup(
  1467. func(e v5HandleEnv) {
  1468. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1469. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1470. },
  1471. nil,
  1472. ),
  1473. },
  1474. {
  1475. name: "set basic service error",
  1476. session: sessionWithInstance(testV5UIN),
  1477. pkt: metaUserPkt(ICQLegacyMetaSetBasic2, v5MetaSetBasicData()),
  1478. setup: v5CaseSetup(
  1479. func(e v5HandleEnv) {
  1480. e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV5UIN, mock.Anything).
  1481. Return(errors.New("fail")).Once()
  1482. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1483. },
  1484. nil,
  1485. ),
  1486. },
  1487. {
  1488. name: "set work success",
  1489. session: sessionWithInstance(testV5UIN),
  1490. pkt: metaUserPkt(ICQLegacyMetaSetWork2, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{
  1491. Company: "co",
  1492. })),
  1493. setup: v5CaseSetup(
  1494. func(e v5HandleEnv) {
  1495. e.Service.EXPECT().UpdateWorkInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1496. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1497. },
  1498. nil,
  1499. ),
  1500. },
  1501. {
  1502. name: "set work parse error",
  1503. session: sessionWithInstance(testV5UIN),
  1504. pkt: metaUserPkt(ICQLegacyMetaSetWork2, nil),
  1505. setup: v5CaseSetup(
  1506. func(e v5HandleEnv) {
  1507. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1508. },
  1509. nil,
  1510. ),
  1511. },
  1512. {
  1513. name: "set more success",
  1514. session: sessionWithInstance(testV5UIN),
  1515. pkt: metaUserPkt(ICQLegacyMetaSetMore2, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo{
  1516. Gender: 1,
  1517. })),
  1518. setup: v5CaseSetup(
  1519. func(e v5HandleEnv) {
  1520. e.Service.EXPECT().UpdateMoreInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1521. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1522. },
  1523. nil,
  1524. ),
  1525. },
  1526. {
  1527. name: "set more parse error",
  1528. session: sessionWithInstance(testV5UIN),
  1529. pkt: metaUserPkt(ICQLegacyMetaSetMore2, nil),
  1530. setup: v5CaseSetup(
  1531. func(e v5HandleEnv) {
  1532. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1533. },
  1534. nil,
  1535. ),
  1536. },
  1537. {
  1538. name: "set about success",
  1539. session: sessionWithInstance(testV5UIN),
  1540. pkt: metaUserPkt(ICQLegacyMetaSetAbout, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{
  1541. Notes: "about me",
  1542. })),
  1543. setup: v5CaseSetup(
  1544. func(e v5HandleEnv) {
  1545. e.Service.EXPECT().SetNotes(mock.Anything, testV5UIN, "about me").Return(nil).Once()
  1546. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1547. },
  1548. nil,
  1549. ),
  1550. },
  1551. {
  1552. name: "set about parse error",
  1553. session: sessionWithInstance(testV5UIN),
  1554. pkt: metaUserPkt(ICQLegacyMetaSetAbout, nil),
  1555. setup: v5CaseSetup(
  1556. func(e v5HandleEnv) {
  1557. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1558. },
  1559. nil,
  1560. ),
  1561. },
  1562. {
  1563. name: "set interests success",
  1564. session: sessionWithInstance(testV5UIN),
  1565. pkt: metaUserPkt(ICQLegacyMetaSetInterests, v5MetaInterestsWireData(t)),
  1566. setup: v5CaseSetup(
  1567. func(e v5HandleEnv) {
  1568. e.Service.EXPECT().SetInterests(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1569. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1570. },
  1571. nil,
  1572. ),
  1573. },
  1574. {
  1575. name: "set interests wrong count",
  1576. session: sessionWithInstance(testV5UIN),
  1577. pkt: metaUserPkt(ICQLegacyMetaSetInterests, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{
  1578. Interests: []struct {
  1579. Code uint16
  1580. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  1581. }{{Code: 1, Keyword: "only"}},
  1582. })),
  1583. setup: v5CaseSetup(
  1584. func(e v5HandleEnv) {
  1585. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1586. },
  1587. nil,
  1588. ),
  1589. },
  1590. {
  1591. name: "set affiliations success",
  1592. session: sessionWithInstance(testV5UIN),
  1593. pkt: metaUserPkt(ICQLegacyMetaSetAffiliations, v5MetaAffiliationsWireData(t)),
  1594. setup: v5CaseSetup(
  1595. func(e v5HandleEnv) {
  1596. e.Service.EXPECT().SetAffiliations(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1597. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1598. },
  1599. nil,
  1600. ),
  1601. },
  1602. {
  1603. name: "set affiliations wrong count",
  1604. session: sessionWithInstance(testV5UIN),
  1605. pkt: metaUserPkt(ICQLegacyMetaSetAffiliations, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
  1606. PastAffiliations: []struct {
  1607. Code uint16
  1608. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  1609. }{{Code: 1, Keyword: "p1"}},
  1610. Affiliations: []struct {
  1611. Code uint16
  1612. Keyword string `oscar:"len_prefix=uint16,nullterm"`
  1613. }{{Code: 2, Keyword: "c1"}},
  1614. })),
  1615. setup: v5CaseSetup(
  1616. func(e v5HandleEnv) {
  1617. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1618. },
  1619. nil,
  1620. ),
  1621. },
  1622. {
  1623. name: "set security success",
  1624. session: sessionWithInstance(testV5UIN),
  1625. pkt: metaUserPkt(ICQLegacyMetaSetSecurity, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions{
  1626. Authorization: 0,
  1627. WebAware: 1,
  1628. })),
  1629. setup: v5CaseSetup(
  1630. func(e v5HandleEnv) {
  1631. e.Service.EXPECT().UpdatePermissions(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1632. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1633. },
  1634. nil,
  1635. ),
  1636. },
  1637. {
  1638. name: "set security parse error",
  1639. session: sessionWithInstance(testV5UIN),
  1640. pkt: metaUserPkt(ICQLegacyMetaSetSecurity, nil),
  1641. setup: v5CaseSetup(
  1642. func(e v5HandleEnv) {
  1643. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1644. },
  1645. nil,
  1646. ),
  1647. },
  1648. {
  1649. name: "set password success",
  1650. session: sessionWithInstance(testV5UIN),
  1651. pkt: metaUserPkt(ICQLegacyMetaSetPass, v5MetaSetPassData("newpass")),
  1652. setup: v5CaseSetup(
  1653. func(e v5HandleEnv) {
  1654. e.Service.EXPECT().SetPassword(mock.Anything, testV5UIN, "", "newpass").Return(nil).Once()
  1655. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1656. },
  1657. nil,
  1658. ),
  1659. },
  1660. {
  1661. name: "set password truncated",
  1662. session: sessionWithInstance(testV5UIN),
  1663. pkt: metaUserPkt(ICQLegacyMetaSetPass, []byte{0x01}),
  1664. setup: v5CaseSetup(
  1665. func(e v5HandleEnv) {
  1666. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1667. },
  1668. nil,
  1669. ),
  1670. },
  1671. {
  1672. name: "set hpcat success",
  1673. session: sessionWithInstance(testV5UIN),
  1674. pkt: metaUserPkt(ICQLegacyMetaSetHPCat, v5MetaSetHPCatData(true, 1, "home")),
  1675. setup: v5CaseSetup(
  1676. func(e v5HandleEnv) {
  1677. e.Service.EXPECT().SetHomepageCategory(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
  1678. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1679. },
  1680. nil,
  1681. ),
  1682. },
  1683. {
  1684. name: "set hpcat data too short",
  1685. session: sessionWithInstance(testV5UIN),
  1686. pkt: metaUserPkt(ICQLegacyMetaSetHPCat, []byte{0x01, 0x02}),
  1687. setup: v5CaseSetup(
  1688. func(e v5HandleEnv) {
  1689. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1690. },
  1691. nil,
  1692. ),
  1693. },
  1694. })
  1695. }
  1696. func TestV5Handler_HandleMetaUser_Search(t *testing.T) {
  1697. runV5HandleCases(t, []v5HandleCase{
  1698. {
  1699. name: "search name found",
  1700. session: sessionWithInstance(testV5UIN),
  1701. pkt: metaUserPkt(ICQLegacyMetaSearchName, v5MetaSearchNameData()),
  1702. setup: v5CaseSetup(
  1703. func(e v5HandleEnv) {
  1704. e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
  1705. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  1706. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1707. },
  1708. nil,
  1709. ),
  1710. },
  1711. {
  1712. name: "search name empty",
  1713. session: sessionWithInstance(testV5UIN),
  1714. pkt: metaUserPkt(ICQLegacyMetaSearchName, v5MetaSearchNameData()),
  1715. setup: v5CaseSetup(
  1716. func(e v5HandleEnv) {
  1717. e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
  1718. Return(nil, nil).Once()
  1719. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1720. },
  1721. nil,
  1722. ),
  1723. },
  1724. {
  1725. name: "search uin found",
  1726. session: sessionWithInstance(testV5UIN),
  1727. pkt: metaUserPkt(ICQLegacyMetaSearchUIN, v5TargetUINData(99999)),
  1728. setup: v5CaseSetup(
  1729. func(e v5HandleEnv) {
  1730. e.Service.EXPECT().GetUserInfoForProtocol(mock.Anything, uint32(99999)).
  1731. Return(&UserInfoResult{UIN: 99999}, nil).Once()
  1732. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1733. },
  1734. nil,
  1735. ),
  1736. },
  1737. {
  1738. name: "search uin data too short",
  1739. session: sessionWithInstance(testV5UIN),
  1740. pkt: metaUserPkt(ICQLegacyMetaSearchUIN, []byte{0x01, 0x02, 0x03}),
  1741. setup: v5CaseSetup(
  1742. func(e v5HandleEnv) {
  1743. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1744. },
  1745. nil,
  1746. ),
  1747. },
  1748. {
  1749. name: "search email found",
  1750. session: sessionWithInstance(testV5UIN),
  1751. pkt: metaUserPkt(ICQLegacyMetaSearchEmail, v5MetaSearchEmailData("a@b.com")),
  1752. setup: v5CaseSetup(
  1753. func(e v5HandleEnv) {
  1754. e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
  1755. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  1756. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1757. },
  1758. nil,
  1759. ),
  1760. },
  1761. {
  1762. name: "search email empty",
  1763. session: sessionWithInstance(testV5UIN),
  1764. pkt: metaUserPkt(ICQLegacyMetaSearchEmail, v5MetaSearchEmailData("a@b.com")),
  1765. setup: v5CaseSetup(
  1766. func(e v5HandleEnv) {
  1767. e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
  1768. Return(nil, nil).Once()
  1769. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1770. },
  1771. nil,
  1772. ),
  1773. },
  1774. {
  1775. name: "search white found",
  1776. session: sessionWithInstance(testV5UIN),
  1777. pkt: metaUserPkt(ICQLegacyMetaSearchWhite, v5LPString("alice")),
  1778. setup: v5CaseSetup(
  1779. func(e v5HandleEnv) {
  1780. e.Service.EXPECT().WhitePagesSearch(mock.Anything, mock.Anything).
  1781. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  1782. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1783. },
  1784. nil,
  1785. ),
  1786. },
  1787. {
  1788. name: "search white no criteria",
  1789. session: sessionWithInstance(testV5UIN),
  1790. pkt: metaUserPkt(ICQLegacyMetaSearchWhite, nil),
  1791. setup: v5CaseSetup(
  1792. func(e v5HandleEnv) {
  1793. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1794. },
  1795. nil,
  1796. ),
  1797. },
  1798. {
  1799. name: "search white2 found",
  1800. session: sessionWithInstance(testV5UIN),
  1801. pkt: metaUserPkt(ICQLegacyMetaSearchWhite2, v5LPString("alice")),
  1802. setup: v5CaseSetup(
  1803. func(e v5HandleEnv) {
  1804. e.Service.EXPECT().SearchByName(mock.Anything, "", "alice", "", "").
  1805. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  1806. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1807. },
  1808. nil,
  1809. ),
  1810. },
  1811. {
  1812. name: "search white2 empty",
  1813. session: sessionWithInstance(testV5UIN),
  1814. pkt: metaUserPkt(ICQLegacyMetaSearchWhite2, v5LPString("alice")),
  1815. setup: v5CaseSetup(
  1816. func(e v5HandleEnv) {
  1817. e.Service.EXPECT().SearchByName(mock.Anything, "", "alice", "", "").
  1818. Return(nil, nil).Once()
  1819. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1820. },
  1821. nil,
  1822. ),
  1823. },
  1824. })
  1825. }
  1826. func TestV5Handler_HandleMetaUser_Unregister(t *testing.T) {
  1827. runV5HandleCases(t, []v5HandleCase{
  1828. {
  1829. name: "unregister success",
  1830. session: sessionWithInstance(testV5UIN),
  1831. pkt: metaUserPkt(ICQLegacyMetaUserUnreg, v5MetaUnregisterData(testV5UIN, "secret")),
  1832. setup: v5CaseSetup(
  1833. func(e v5HandleEnv) {
  1834. e.Service.EXPECT().DeleteUser(mock.Anything, testV5UIN, "secret").Return(nil).Once()
  1835. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1836. },
  1837. nil,
  1838. ),
  1839. },
  1840. {
  1841. name: "unregister data too short",
  1842. session: sessionWithInstance(testV5UIN),
  1843. pkt: metaUserPkt(ICQLegacyMetaUserUnreg, []byte{0x01, 0x02, 0x03, 0x04, 0x05}),
  1844. setup: v5CaseSetup(
  1845. func(e v5HandleEnv) {
  1846. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1847. },
  1848. nil,
  1849. ),
  1850. },
  1851. })
  1852. }
  1853. func TestV5Handler_HandleMetaUser_Default(t *testing.T) {
  1854. runV5HandleCases(t, []v5HandleCase{
  1855. {
  1856. name: "unknown sub-command",
  1857. session: sessionWithInstance(testV5UIN),
  1858. pkt: metaUserPkt(0xFFFF, nil),
  1859. setup: v5CaseSetup(
  1860. func(e v5HandleEnv) {
  1861. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  1862. },
  1863. nil,
  1864. ),
  1865. },
  1866. })
  1867. }
  1868. func TestV5Handler_HandleVisibleList(t *testing.T) {
  1869. runV5HandleCases(t, []v5HandleCase{
  1870. {
  1871. name: "valid list",
  1872. session: sessionWithInstance(testV5UIN),
  1873. pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, v5ContactListData([]uint32{11111})),
  1874. setup: v5CaseSetup(
  1875. func(e v5HandleEnv) {
  1876. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  1877. Run(func(_ *LegacySession, p []byte) {
  1878. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  1879. }).Return(nil).Once()
  1880. },
  1881. func(e v5HandleEnv, err error) {
  1882. assert.NoError(e.T, err)
  1883. assert.True(t, e.Session.IsOnVisibleList(11111))
  1884. },
  1885. ),
  1886. },
  1887. {
  1888. name: "nil session",
  1889. pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, v5ContactListData([]uint32{11111})),
  1890. setup: v5CaseSetup(
  1891. func(e v5HandleEnv) {
  1892. expectV5NotConnected(e.Sender)
  1893. },
  1894. nil,
  1895. ),
  1896. },
  1897. {
  1898. name: "parse error",
  1899. session: sessionWithInstance(testV5UIN),
  1900. pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, nil),
  1901. setup: v5CaseSetup(
  1902. func(e v5HandleEnv) {
  1903. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1904. },
  1905. func(e v5HandleEnv, err error) {
  1906. assert.NoError(e.T, err)
  1907. assert.False(t, e.Session.IsOnVisibleList(11111))
  1908. },
  1909. ),
  1910. },
  1911. })
  1912. }
  1913. func TestV5Handler_HandleInvisibleList(t *testing.T) {
  1914. runV5HandleCases(t, []v5HandleCase{
  1915. {
  1916. name: "valid list",
  1917. session: sessionWithInstance(testV5UIN),
  1918. pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, v5ContactListData([]uint32{22222})),
  1919. setup: v5CaseSetup(
  1920. func(e v5HandleEnv) {
  1921. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1922. },
  1923. func(e v5HandleEnv, err error) {
  1924. assert.NoError(e.T, err)
  1925. assert.True(t, e.Session.IsOnInvisibleList(22222))
  1926. },
  1927. ),
  1928. },
  1929. {
  1930. name: "nil session",
  1931. pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, v5ContactListData([]uint32{22222})),
  1932. setup: v5CaseSetup(
  1933. func(e v5HandleEnv) {
  1934. expectV5NotConnected(e.Sender)
  1935. },
  1936. nil,
  1937. ),
  1938. },
  1939. {
  1940. name: "parse error",
  1941. session: sessionWithInstance(testV5UIN),
  1942. pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, nil),
  1943. setup: v5CaseSetup(
  1944. func(e v5HandleEnv) {
  1945. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1946. },
  1947. func(e v5HandleEnv, err error) {
  1948. assert.NoError(e.T, err)
  1949. assert.False(t, e.Session.IsOnInvisibleList(22222))
  1950. },
  1951. ),
  1952. },
  1953. })
  1954. }
  1955. func TestV5Handler_HandleChangeVILists(t *testing.T) {
  1956. runV5HandleCases(t, []v5HandleCase{
  1957. {
  1958. name: "valid action",
  1959. session: sessionWithInstance(testV5UIN),
  1960. pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, func() []byte {
  1961. b := make([]byte, 5)
  1962. b[0] = 1
  1963. binary.LittleEndian.PutUint32(b[1:], 33333)
  1964. return b
  1965. }()),
  1966. setup: v5CaseSetup(
  1967. func(e v5HandleEnv) {
  1968. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1969. },
  1970. nil,
  1971. ),
  1972. },
  1973. {
  1974. name: "nil session",
  1975. pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, func() []byte {
  1976. b := make([]byte, 5)
  1977. b[0] = 1
  1978. binary.LittleEndian.PutUint32(b[1:], 33333)
  1979. return b
  1980. }()),
  1981. setup: v5CaseSetup(
  1982. func(e v5HandleEnv) {
  1983. expectV5NotConnected(e.Sender)
  1984. },
  1985. nil,
  1986. ),
  1987. },
  1988. {
  1989. name: "data too short",
  1990. session: sessionWithInstance(testV5UIN),
  1991. pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, []byte{1}),
  1992. setup: v5CaseSetup(
  1993. func(e v5HandleEnv) {
  1994. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  1995. },
  1996. nil,
  1997. ),
  1998. },
  1999. })
  2000. }
  2001. func TestV5Handler_HandleOldSearchUIN(t *testing.T) {
  2002. runV5HandleCases(t, []v5HandleCase{
  2003. {
  2004. name: "user found",
  2005. session: sessionWithInstance(testV5UIN),
  2006. pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
  2007. setup: v5CaseSetup(
  2008. func(e v5HandleEnv) {
  2009. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  2010. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  2011. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
  2012. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // found
  2013. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2014. Run(func(_ *LegacySession, p []byte) {
  2015. assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
  2016. }).Return(nil).Once()
  2017. },
  2018. nil,
  2019. ),
  2020. },
  2021. {
  2022. name: "user not found",
  2023. session: sessionWithInstance(testV5UIN),
  2024. pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
  2025. setup: v5CaseSetup(
  2026. func(e v5HandleEnv) {
  2027. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  2028. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
  2029. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2030. Run(func(_ *LegacySession, p []byte) {
  2031. assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
  2032. }).Return(nil).Once()
  2033. },
  2034. nil,
  2035. ),
  2036. },
  2037. {
  2038. name: "service error",
  2039. session: sessionWithInstance(testV5UIN),
  2040. pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
  2041. setup: v5CaseSetup(
  2042. func(e v5HandleEnv) {
  2043. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  2044. Return(nil, errors.New("fail")).Once()
  2045. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2046. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2047. Run(func(_ *LegacySession, p []byte) {
  2048. assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
  2049. }).Return(nil).Once()
  2050. },
  2051. nil,
  2052. ),
  2053. },
  2054. {
  2055. name: "data too short",
  2056. session: sessionWithInstance(testV5UIN),
  2057. pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, []byte{1, 2}),
  2058. setup: v5CaseSetup(
  2059. func(e v5HandleEnv) {
  2060. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2061. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2062. Run(func(_ *LegacySession, p []byte) {
  2063. assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
  2064. }).Return(nil).Once()
  2065. },
  2066. nil,
  2067. ),
  2068. },
  2069. {
  2070. name: "nil session",
  2071. pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
  2072. setup: v5CaseSetup(
  2073. func(e v5HandleEnv) {
  2074. expectV5NotConnected(e.Sender)
  2075. },
  2076. nil,
  2077. ),
  2078. },
  2079. })
  2080. }
  2081. func TestV5Handler_HandleOldSearch(t *testing.T) {
  2082. runV5HandleCases(t, []v5HandleCase{
  2083. {
  2084. name: "any data",
  2085. session: sessionWithInstance(testV5UIN),
  2086. pkt: defaultV5Pkt(ICQLegacyCmdSearchUser, []byte{0x01}),
  2087. setup: v5CaseSetup(
  2088. func(e v5HandleEnv) {
  2089. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2090. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2091. Run(func(_ *LegacySession, p []byte) {
  2092. assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
  2093. }).Return(nil).Once()
  2094. },
  2095. nil,
  2096. ),
  2097. },
  2098. {
  2099. name: "nil session",
  2100. pkt: defaultV5Pkt(ICQLegacyCmdSearchUser, []byte{0x01}),
  2101. setup: v5CaseSetup(
  2102. func(e v5HandleEnv) {
  2103. expectV5NotConnected(e.Sender)
  2104. },
  2105. nil,
  2106. ),
  2107. },
  2108. })
  2109. }
  2110. func TestV5Handler_HandleOldInfoReq(t *testing.T) {
  2111. runV5HandleCases(t, []v5HandleCase{
  2112. {
  2113. name: "user found",
  2114. session: sessionWithInstance(testV5UIN),
  2115. pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
  2116. setup: v5CaseSetup(
  2117. func(e v5HandleEnv) {
  2118. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  2119. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  2120. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
  2121. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2122. Run(func(_ *LegacySession, p []byte) {
  2123. assert.Equal(t, ICQLegacySrvInfoReply, v5ServerCommand(p))
  2124. }).Return(nil).Once()
  2125. },
  2126. nil,
  2127. ),
  2128. },
  2129. {
  2130. name: "user not found",
  2131. session: sessionWithInstance(testV5UIN),
  2132. pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
  2133. setup: v5CaseSetup(
  2134. func(e v5HandleEnv) {
  2135. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  2136. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2137. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2138. Run(func(_ *LegacySession, p []byte) {
  2139. assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
  2140. }).Return(nil).Once()
  2141. },
  2142. nil,
  2143. ),
  2144. },
  2145. {
  2146. name: "service error",
  2147. session: sessionWithInstance(testV5UIN),
  2148. pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
  2149. setup: v5CaseSetup(
  2150. func(e v5HandleEnv) {
  2151. e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
  2152. Return(nil, errors.New("fail")).Once()
  2153. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2154. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2155. Run(func(_ *LegacySession, p []byte) {
  2156. assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
  2157. }).Return(nil).Once()
  2158. },
  2159. nil,
  2160. ),
  2161. },
  2162. {
  2163. name: "data too short",
  2164. session: sessionWithInstance(testV5UIN),
  2165. pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, []byte{1, 2}),
  2166. setup: v5CaseSetup(
  2167. func(e v5HandleEnv) {
  2168. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2169. },
  2170. nil,
  2171. ),
  2172. },
  2173. {
  2174. name: "nil session",
  2175. pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
  2176. setup: v5CaseSetup(
  2177. func(e v5HandleEnv) {
  2178. expectV5NotConnected(e.Sender)
  2179. },
  2180. nil,
  2181. ),
  2182. },
  2183. })
  2184. }
  2185. func TestV5Handler_HandleOldExtInfoReq(t *testing.T) {
  2186. runV5HandleCases(t, []v5HandleCase{
  2187. {
  2188. name: "user found",
  2189. session: sessionWithInstance(testV5UIN),
  2190. pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
  2191. setup: v5CaseSetup(
  2192. func(e v5HandleEnv) {
  2193. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
  2194. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2195. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2196. Run(func(_ *LegacySession, p []byte) {
  2197. assert.Equal(t, ICQLegacySrvExtInfoReply, v5ServerCommand(p))
  2198. }).Return(nil).Once()
  2199. },
  2200. nil,
  2201. ),
  2202. },
  2203. {
  2204. name: "user not found",
  2205. session: sessionWithInstance(testV5UIN),
  2206. pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
  2207. setup: v5CaseSetup(
  2208. func(e v5HandleEnv) {
  2209. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  2210. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2211. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2212. Run(func(_ *LegacySession, p []byte) {
  2213. assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
  2214. }).Return(nil).Once()
  2215. },
  2216. nil,
  2217. ),
  2218. },
  2219. {
  2220. name: "service error",
  2221. session: sessionWithInstance(testV5UIN),
  2222. pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
  2223. setup: v5CaseSetup(
  2224. func(e v5HandleEnv) {
  2225. e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).
  2226. Return(nil, errors.New("fail")).Once()
  2227. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2228. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2229. Run(func(_ *LegacySession, p []byte) {
  2230. assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
  2231. }).Return(nil).Once()
  2232. },
  2233. nil,
  2234. ),
  2235. },
  2236. {
  2237. name: "data too short",
  2238. session: sessionWithInstance(testV5UIN),
  2239. pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, []byte{1}),
  2240. setup: v5CaseSetup(
  2241. func(e v5HandleEnv) {
  2242. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
  2243. },
  2244. nil,
  2245. ),
  2246. },
  2247. {
  2248. name: "nil session",
  2249. pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
  2250. setup: v5CaseSetup(
  2251. func(e v5HandleEnv) {
  2252. expectV5NotConnected(e.Sender)
  2253. },
  2254. nil,
  2255. ),
  2256. },
  2257. })
  2258. }
  2259. func TestV5Handler_HandleAckNewUIN(t *testing.T) {
  2260. runV5HandleCases(t, []v5HandleCase{
  2261. {
  2262. name: "with session",
  2263. session: sessionWithInstance(testV5UIN),
  2264. pkt: defaultV5Pkt(ICQLegacyCmdRegNewUser, nil),
  2265. setup: v5CaseSetup(
  2266. func(e v5HandleEnv) {
  2267. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
  2268. Run(func(_ *LegacySession, p []byte) {
  2269. assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
  2270. }).Return(nil).Once()
  2271. },
  2272. nil,
  2273. ),
  2274. },
  2275. {
  2276. name: "nil session",
  2277. pkt: defaultV5Pkt(ICQLegacyCmdRegNewUser, nil),
  2278. },
  2279. })
  2280. }
  2281. func TestV5Handler_HandleDirectWhiteSearch(t *testing.T) {
  2282. runV5HandleCases(t, []v5HandleCase{
  2283. {
  2284. name: "results found",
  2285. session: sessionWithInstance(testV5UIN),
  2286. pkt: defaultV5Pkt(0x0532, v5LPString("alice")),
  2287. setup: v5CaseSetup(
  2288. func(e v5HandleEnv) {
  2289. e.Service.EXPECT().WhitePagesSearch(mock.Anything, mock.Anything).
  2290. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  2291. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2) // ack + result
  2292. },
  2293. nil,
  2294. ),
  2295. },
  2296. {
  2297. name: "nil session",
  2298. pkt: defaultV5Pkt(0x0532, v5LPString("alice")),
  2299. setup: v5CaseSetup(
  2300. func(e v5HandleEnv) {
  2301. expectV5NotConnected(e.Sender)
  2302. },
  2303. nil,
  2304. ),
  2305. },
  2306. })
  2307. }
  2308. func TestV5Handler_HandleDirectNameSearch(t *testing.T) {
  2309. nameData := append(append(v5LPString("nick"), v5LPString("first")...), v5LPString("last")...)
  2310. runV5HandleCases(t, []v5HandleCase{
  2311. {
  2312. name: "results found",
  2313. session: sessionWithInstance(testV5UIN),
  2314. pkt: defaultV5Pkt(0x0514, nameData),
  2315. setup: v5CaseSetup(
  2316. func(e v5HandleEnv) {
  2317. e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
  2318. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  2319. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
  2320. },
  2321. nil,
  2322. ),
  2323. },
  2324. {
  2325. name: "nil session",
  2326. pkt: defaultV5Pkt(0x0514, nameData),
  2327. setup: v5CaseSetup(
  2328. func(e v5HandleEnv) {
  2329. expectV5NotConnected(e.Sender)
  2330. },
  2331. nil,
  2332. ),
  2333. },
  2334. })
  2335. }
  2336. func TestV5Handler_HandleDirectUINSearch(t *testing.T) {
  2337. runV5HandleCases(t, []v5HandleCase{
  2338. {
  2339. name: "user found",
  2340. session: sessionWithInstance(testV5UIN),
  2341. pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
  2342. setup: v5CaseSetup(
  2343. func(e v5HandleEnv) {
  2344. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
  2345. Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
  2346. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
  2347. },
  2348. nil,
  2349. ),
  2350. },
  2351. {
  2352. name: "user not found",
  2353. session: sessionWithInstance(testV5UIN),
  2354. pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
  2355. setup: v5CaseSetup(
  2356. func(e v5HandleEnv) {
  2357. e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).Return(nil, nil).Once()
  2358. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
  2359. },
  2360. nil,
  2361. ),
  2362. },
  2363. {
  2364. name: "nil session",
  2365. pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
  2366. setup: v5CaseSetup(
  2367. func(e v5HandleEnv) {
  2368. expectV5NotConnected(e.Sender)
  2369. },
  2370. nil,
  2371. ),
  2372. },
  2373. })
  2374. }
  2375. func TestV5Handler_HandleDirectEmailSearch(t *testing.T) {
  2376. runV5HandleCases(t, []v5HandleCase{
  2377. {
  2378. name: "results found",
  2379. session: sessionWithInstance(testV5UIN),
  2380. pkt: defaultV5Pkt(0x0528, v5LPString("a@b.com")),
  2381. setup: v5CaseSetup(
  2382. func(e v5HandleEnv) {
  2383. e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
  2384. Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
  2385. e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
  2386. },
  2387. nil,
  2388. ),
  2389. },
  2390. {
  2391. name: "nil session",
  2392. pkt: defaultV5Pkt(0x0528, v5LPString("a@b.com")),
  2393. setup: v5CaseSetup(
  2394. func(e v5HandleEnv) {
  2395. expectV5NotConnected(e.Sender)
  2396. },
  2397. nil,
  2398. ),
  2399. },
  2400. })
  2401. }