| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528 |
- package icq_legacy
- import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "log/slog"
- "net"
- "strconv"
- "testing"
- "time"
- "github.com/mk6i/open-oscar-server/config"
- "github.com/mk6i/open-oscar-server/state"
- "github.com/mk6i/open-oscar-server/wire"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- )
- const (
- testV5UIN = uint32(12345)
- testV5SessionID = uint32(0xBEEF)
- testV5Seq1 = uint16(42)
- testV5Seq2 = uint16(7)
- )
- var testV5Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4000}
- // noopSessionRegistry satisfies SessionRegistry for tests that remove legacy sessions.
- type noopSessionRegistry struct{}
- func (noopSessionRegistry) RemoveSession(*state.Session) {}
- func newTestV5SessionManager() *LegacySessionManager {
- return NewLegacySessionManager(noopSessionRegistry{}, config.ICQLegacyConfig{}, slog.Default())
- }
- // newTestLegacySessionManager is shared by V1–V4 handler tests.
- func newTestLegacySessionManager() *LegacySessionManager {
- return newTestV5SessionManager()
- }
- func newTestV5Handler(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V5Handler {
- t.Helper()
- if sessions == nil {
- sessions = newTestV5SessionManager()
- }
- return NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), slog.Default())
- }
- func buildPlainV5ClientPacket(pkt V5ClientPacket) []byte {
- size := 24 + len(pkt.Data)
- buf := make([]byte, size)
- binary.LittleEndian.PutUint16(buf[0:2], pkt.Version)
- binary.LittleEndian.PutUint32(buf[2:6], pkt.Zero)
- binary.LittleEndian.PutUint32(buf[6:10], pkt.UIN)
- binary.LittleEndian.PutUint32(buf[10:14], pkt.SessionID)
- binary.LittleEndian.PutUint16(buf[14:16], pkt.Command)
- binary.LittleEndian.PutUint16(buf[16:18], pkt.SeqNum1)
- binary.LittleEndian.PutUint16(buf[18:20], pkt.SeqNum2)
- if len(pkt.Data) > 0 {
- copy(buf[24:], pkt.Data)
- }
- return buf
- }
- // buildEncryptedV5ClientPacket builds a wire-format V5 client packet for Handle().
- // Handle applies DecryptV5Packet, which is an XOR-based involution, so we pre-apply
- // DecryptV5Packet to plaintext to produce bytes that decrypt back to the intended fields.
- func buildEncryptedV5ClientPacket(t *testing.T, pkt V5ClientPacket) []byte {
- t.Helper()
- buf := buildPlainV5ClientPacket(pkt)
- // Place a valid checkcode at 0x14 (required for decryption key derivation).
- cc := calculateV5CheckCode(buf)
- binary.LittleEndian.PutUint32(buf[20:24], cc)
- DecryptV5Packet(buf, 0)
- // Verify Handle's decrypt path recovers the intended command.
- verify := make([]byte, len(buf))
- copy(verify, buf)
- DecryptV5Packet(verify, 0)
- require.Equal(t, pkt.Command, binary.LittleEndian.Uint16(verify[14:16]),
- "V5 test packet builder failed for command 0x%04X", pkt.Command)
- return buf
- }
- func v5ServerCommand(packet []byte) uint16 {
- if len(packet) < 9 {
- return 0
- }
- return binary.LittleEndian.Uint16(packet[7:9])
- }
- func v5ServerPacketData(packet []byte) []byte {
- if len(packet) <= 21 {
- return nil
- }
- return packet[21:]
- }
- func v5ServerSeq1(packet []byte) uint16 {
- if len(packet) < 11 {
- return 0
- }
- return binary.LittleEndian.Uint16(packet[9:11])
- }
- func v5ServerSeq2(packet []byte) uint16 {
- if len(packet) < 13 {
- return 0
- }
- return binary.LittleEndian.Uint16(packet[11:13])
- }
- func newTestV5HandlerWithDispatcher(t *testing.T, sender PacketSender, svc LegacyService, sessions *LegacySessionManager) *V5Handler {
- t.Helper()
- logger := slog.Default()
- v1 := NewV1Handler(sessions, svc, sender, logger)
- v2 := NewV2Handler(sessions, svc, sender, NewV2PacketBuilder(), logger)
- v3 := NewV3Handler(sessions, svc, sender, NewV3PacketBuilder(sessions, nil), logger)
- v4 := NewV4Handler(sessions, svc, sender, NewV4PacketBuilder(sessions, nil), logger)
- v5 := NewV5Handler(sessions, svc, sender, NewV5PacketBuilder(sessions, nil), logger)
- cfg := config.ICQLegacyConfig{SupportedVersions: []int{3, 4, 5}}
- dispatcher := NewProtocolDispatcher(v1, v2, v3, v4, v5, cfg, logger)
- v5.SetDispatcher(dispatcher)
- return v5
- }
- func sessionWithDirectConn(uin uint32, tcpPort, internalIP uint32, dcType uint8) *LegacySession {
- s := sessionWithInstance(uin)
- s.SetDirectConnectionInfo(tcpPort, internalIP, 5, dcType)
- return s
- }
- func sessionWithInstance(uin uint32) *LegacySession {
- s := newTestLegacySession(uin, legacySessionOptOSCARSess, func(ls *LegacySession) {
- ls.SessionID = testV5SessionID
- ls.Version = ICQLegacyVersionV5
- })
- return s
- }
- func expectV5NotConnected(sender *mockPacketSender) {
- sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- if got := v5ServerCommand(p); got != ICQLegacySrvNotConnected {
- panic(fmt.Sprintf("got command 0x%04X, want NOT_CONNECTED", got))
- }
- }).Return(nil).Once()
- }
- func registerSession(mgr *LegacySessionManager, session *LegacySession) {
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
- mgr.sessions[session.UIN] = session
- if session.Addr != nil {
- mgr.addrIndex[session.Addr.String()] = session
- }
- }
- func defaultV5Pkt(command uint16, data []byte) V5ClientPacket {
- return V5ClientPacket{
- Version: ICQLegacyVersionV5,
- UIN: testV5UIN,
- SessionID: testV5SessionID,
- Command: command,
- SeqNum1: testV5Seq1,
- SeqNum2: testV5Seq2,
- Data: data,
- }
- }
- func v5LPString(s string) []byte {
- buf := new(bytes.Buffer)
- writeLegacyString(buf, s)
- return buf.Bytes()
- }
- func v5GetDepsData(uin uint32, password string) []byte {
- pwd := []byte(password)
- buf := make([]byte, 4+2+len(pwd))
- binary.LittleEndian.PutUint32(buf[0:4], uin)
- binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
- copy(buf[6:], pwd)
- return buf
- }
- func v5LoginData(password string, minimal bool) []byte {
- pwd := []byte(password)
- if minimal {
- buf := make([]byte, 4+4+2+len(pwd))
- binary.LittleEndian.PutUint32(buf[4:8], 4000)
- binary.LittleEndian.PutUint16(buf[8:10], uint16(len(pwd)))
- copy(buf[10:], pwd)
- return buf
- }
- buf := make([]byte, 4+4+2+len(pwd)+4+4+1+4+4+28)
- off := 0
- off += 4 // time
- binary.LittleEndian.PutUint32(buf[off:], 4000)
- off += 4
- binary.LittleEndian.PutUint16(buf[off:], uint16(len(pwd)))
- off += 2
- copy(buf[off:], pwd)
- off += len(pwd)
- binary.LittleEndian.PutUint32(buf[off:], 0x98)
- off += 4
- binary.LittleEndian.PutUint32(buf[off:], 0x7F000001)
- off += 4
- buf[off] = 0
- off++
- binary.LittleEndian.PutUint32(buf[off:], 0)
- off += 4
- binary.LittleEndian.PutUint32(buf[off:], 0)
- return buf
- }
- func v5ContactListData(uins []uint32) []byte {
- buf := make([]byte, 1+4*len(uins))
- buf[0] = byte(len(uins))
- for i, u := range uins {
- binary.LittleEndian.PutUint32(buf[1+4*i:], u)
- }
- return buf
- }
- func v5MessageData(toUIN uint32, msgType uint16, message string) []byte {
- msg := []byte(message)
- buf := make([]byte, 4+2+2+len(msg))
- binary.LittleEndian.PutUint32(buf[0:4], toUIN)
- binary.LittleEndian.PutUint16(buf[4:6], msgType)
- binary.LittleEndian.PutUint16(buf[6:8], uint16(len(msg)))
- copy(buf[8:], msg)
- return buf
- }
- func v5MetaUserData(subCmd uint16, subData []byte) []byte {
- buf := make([]byte, 2+len(subData))
- binary.LittleEndian.PutUint16(buf[0:2], subCmd)
- copy(buf[2:], subData)
- return buf
- }
- func metaUserPkt(subCmd uint16, subData []byte) V5ClientPacket {
- return defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(subCmd, subData))
- }
- func v5MetaWireMarshal(t *testing.T, body any) []byte {
- t.Helper()
- var buf bytes.Buffer
- require.NoError(t, wire.MarshalLE(body, &buf))
- return buf.Bytes()
- }
- func v5MetaSetBasicData() []byte {
- buf := new(bytes.Buffer)
- for _, s := range []string{
- "nick", "first", "last", "a@b.com", "", "", "city", "ST",
- "phone", "fax", "addr", "cell", "zip",
- } {
- writeLegacyString(buf, s)
- }
- tail := []byte{0x01, 0x00, 0, 1} // country=1, gmt=0, publish=1
- return append(buf.Bytes(), tail...)
- }
- func v5MetaUnregisterData(uin uint32, password string) []byte {
- pwd := []byte(password)
- buf := make([]byte, 4+2+len(pwd))
- binary.LittleEndian.PutUint32(buf[0:4], uin)
- binary.LittleEndian.PutUint16(buf[4:6], uint16(len(pwd)))
- copy(buf[6:], pwd)
- return buf
- }
- func v5MetaSetPassData(password string) []byte {
- pwd := []byte(password)
- buf := make([]byte, 2+len(pwd))
- binary.LittleEndian.PutUint16(buf[0:2], uint16(len(pwd)))
- copy(buf[2:], pwd)
- return buf
- }
- func v5MetaSetHPCatData(enabled bool, index uint16, desc string) []byte {
- d := []byte(desc)
- buf := make([]byte, 5+len(d))
- if enabled {
- buf[0] = 1
- }
- binary.LittleEndian.PutUint16(buf[1:3], index)
- binary.LittleEndian.PutUint16(buf[3:5], uint16(len(d)))
- copy(buf[5:], d)
- return buf
- }
- func v5MetaSearchNameData() []byte {
- return append(append(v5LPString("nick"), v5LPString("first")...), v5LPString("last")...)
- }
- func v5MetaSearchEmailData(email string) []byte {
- return v5LPString(email)
- }
- func v5MetaInterestsWireData(t *testing.T) []byte {
- t.Helper()
- return v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{
- Interests: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{
- {Code: 1, Keyword: "kw1"},
- {Code: 2, Keyword: "kw2"},
- {Code: 3, Keyword: "kw3"},
- {Code: 4, Keyword: "kw4"},
- },
- })
- }
- func v5MetaAffiliationsWireData(t *testing.T) []byte {
- t.Helper()
- return v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
- PastAffiliations: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{
- {Code: 1, Keyword: "p1"},
- {Code: 2, Keyword: "p2"},
- {Code: 3, Keyword: "p3"},
- },
- Affiliations: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{
- {Code: 4, Keyword: "c1"},
- {Code: 5, Keyword: "c2"},
- {Code: 6, Keyword: "c3"},
- },
- })
- }
- func v5TargetUINData(target uint32) []byte {
- buf := make([]byte, 4)
- binary.LittleEndian.PutUint32(buf, target)
- return buf
- }
- func testAuthSuccessInstance() *state.SessionInstance {
- return newTestOSCARInstance(state.DisplayScreenName(strconv.FormatUint(uint64(testV5UIN), 10)))
- }
- func testMinimalUser() *state.User {
- u := &state.User{}
- u.ICQInfo.Basic.Nickname = "nick"
- u.ICQInfo.Basic.FirstName = "first"
- u.ICQInfo.Basic.LastName = "last"
- return u
- }
- // v5HandleEnv groups per-case dependencies for runV5HandleCases.
- type v5HandleEnv struct {
- T *testing.T
- Sender *mockPacketSender
- Service *mockLegacyService
- Sessions *LegacySessionManager
- Session *LegacySession // same pointer passed to Handle
- }
- // v5HandleCase is one row in a table-driven V5Handler.Handle test.
- type v5HandleCase struct {
- name string
- session *LegacySession
- pkt V5ClientPacket
- // packet, when set, supplies wire bytes instead of encrypting pkt.
- packet func(t *testing.T) []byte
- // setup configures mocks and session manager before Handle; the returned
- // function validates the Handle result (and optional post-conditions).
- setup func(v5HandleEnv) func(error)
- }
- // v5CaseSetup runs pre-Handle wiring and returns the post-Handle check.
- // If after is nil, Handle is expected to return nil.
- func v5CaseSetup(pre func(v5HandleEnv), after func(e v5HandleEnv, err error)) func(v5HandleEnv) func(error) {
- return func(e v5HandleEnv) func(error) {
- if pre != nil {
- pre(e)
- }
- return func(err error) {
- if after != nil {
- after(e, err)
- return
- }
- assert.NoError(e.T, err)
- }
- }
- }
- func runV5HandleCases(t *testing.T, cases []v5HandleCase) {
- t.Helper()
- for _, tt := range cases {
- t.Run(tt.name, func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- e := v5HandleEnv{
- T: t,
- Sender: sender,
- Service: svc,
- Sessions: sessions,
- Session: tt.session,
- }
- var check func(error)
- if tt.setup != nil {
- check = tt.setup(e)
- } else {
- check = func(err error) { assert.NoError(t, err) }
- }
- h := newTestV5Handler(t, sender, svc, sessions)
- var wire []byte
- if tt.packet != nil {
- wire = tt.packet(t)
- } else {
- wire = buildEncryptedV5ClientPacket(t, tt.pkt)
- }
- check(h.Handle(tt.session, testV5Addr, wire))
- })
- }
- }
- func TestV5Handler_Handle(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "packet too short",
- packet: func(_ *testing.T) []byte { return []byte{0x01, 0x02, 0x03} },
- setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
- require.Error(e.T, err)
- assert.Contains(e.T, err.Error(), "V5 packet too short")
- }),
- },
- {
- name: "nil session disallowed command",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
- setup: v5CaseSetup(func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
- }).Return(nil).Once()
- }, nil),
- },
- {
- name: "nil session ACK allowed",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
- },
- {
- name: "session state updated",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
- setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.Equal(t, testV5Seq1, e.Session.SeqNumClient)
- assert.WithinDuration(e.T, time.Now(), e.Session.GetLastActivity(), time.Second)
- }),
- },
- {
- name: "unknown command",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0xFFFF, nil),
- setup: v5CaseSetup(func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- }, nil),
- },
- {
- name: "inline ACK command",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdAck, nil),
- },
- })
- }
- func TestV5Handler_HandleFirstLogin(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "with session_id2 in data",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdFirstLogin, func() []byte {
- b := make([]byte, 4)
- binary.LittleEndian.PutUint32(b, 0xCAFEBABE)
- return b
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- assert.GreaterOrEqual(t, len(p), 28)
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "empty data",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdFirstLogin, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleGetDeps(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid credentials",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "secret")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "secret").Return(true, nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "invalid credentials",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "wrong")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "wrong").Return(false, nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, v5GetDepsData(testV5UIN, "secret")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ValidateCredentials(mock.Anything, testV5UIN, "secret").
- Return(false, errors.New("db down")).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "packet too short",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdGetDeps, []byte{0x01}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleLogin(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "auth success",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("secret", true)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
- return req.UIN == testV5UIN && req.Password == "secret"
- })).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
- e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
- Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.NotNil(t, e.Sessions.GetSession(testV5UIN))
- },
- ),
- },
- {
- name: "bad password",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("wrong", true)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
- Return(&AuthResult{Success: false, ErrorCode: 0x0001}, nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "auth service error",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdLogin, v5LoginData("secret", true)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.Anything).
- Return(nil, errors.New("auth failed")).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "packet too short",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdLogin, []byte{0, 0, 0}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once() // login ACK
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "truncated password field",
- session: nil,
- pkt: defaultV5Pkt(ICQLegacyCmdLogin, func() []byte {
- buf := make([]byte, 12)
- binary.LittleEndian.PutUint32(buf[4:8], 4000)
- binary.LittleEndian.PutUint16(buf[8:10], 20) // claims 20 bytes but only 2 available
- return buf
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvWrongPasswd, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleContactList(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid contact list",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData([]uint32{99999})),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
- Return(&ContactListResult{}, nil).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData(nil)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "parse error empty data",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdContactList, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdContactList, v5ContactListData(nil)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvUserListDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandlePing(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "keep alive",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "keep alive 2",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive2, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdKeepAlive, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).
- Run(func(_ *net.UDPAddr, p []byte) {
- assert.Equal(t, ICQLegacySrvNotConnected, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleLogoff(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "active session",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdLogoff, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- registerSession(e.Sessions, e.Session)
- e.Service.EXPECT().NotifyUserOffline(mock.Anything, testV5UIN).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.Nil(t, e.Sessions.GetSession(testV5UIN))
- },
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdLogoff, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleSetStatus(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid status change",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
- b := make([]byte, 4)
- binary.LittleEndian.PutUint32(b, 0x00000001)
- return b
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
- Return(&StatusChangeResult{}, nil).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.Equal(t, uint32(0x00000001), e.Session.GetStatus())
- },
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
- b := make([]byte, 4)
- binary.LittleEndian.PutUint32(b, 1)
- return b
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSetStatus, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMessage(t *testing.T) {
- target := sessionWithInstance(99999)
- target.Addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 4001}
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "online target",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- registerSession(e.Sessions, target)
- e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
- Return(&MessageResult{TargetOnline: true}, nil).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
- Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "offline stored",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
- Return(&MessageResult{StoredOffline: true}, nil).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdThruServer, []byte{1, 2, 3}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdThruServer, v5MessageData(99999, 0x0001, "hi")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessMessage(mock.Anything, mock.Anything, mock.Anything).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleUserAdd(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "success",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
- Return(&UserAddResult{}, nil).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.Contains(t, e.Session.GetContactList(), uint32(99999))
- },
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, []byte{1, 2}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_sendV5UserOnline(t *testing.T) {
- const (
- onlineUIN = uint32(67890)
- tcpPort = uint32(5190)
- internalIP = uint32(0x7F000001)
- dcType = uint8(2)
- userStatus = uint32(0x00000010)
- )
- t.Run("V5 peer includes connection info", func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- onlineSess := sessionWithDirectConn(onlineUIN, tcpPort, internalIP, dcType)
- registerSession(sessions, onlineSess)
- recipient := sessionWithInstance(testV5UIN)
- wantExtIP := onlineSess.GetExternalIP()
- sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
- if v5ServerCommand(p) != ICQLegacySrvUserOnline {
- return false
- }
- data := v5ServerPacketData(p)
- return len(data) >= 17 &&
- binary.LittleEndian.Uint32(data[0:4]) == onlineUIN &&
- binary.LittleEndian.Uint32(data[4:8]) == wantExtIP &&
- binary.LittleEndian.Uint32(data[8:12]) == tcpPort &&
- binary.LittleEndian.Uint32(data[12:16]) == internalIP &&
- data[16] == dcType
- })).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, sessions)
- assert.NoError(t, h.sendV5UserOnline(recipient, onlineUIN, userStatus))
- })
- t.Run("non-V5 online user zeros connection info", func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- onlineSess := newTestLegacySession(onlineUIN, legacySessionOptVersion(ICQLegacyVersionV3))
- onlineSess.SetDirectConnectionInfo(tcpPort, internalIP, 5, dcType)
- registerSession(sessions, onlineSess)
- recipient := sessionWithInstance(testV5UIN)
- sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
- len(data) >= 17 &&
- binary.LittleEndian.Uint32(data[4:8]) == 0 &&
- binary.LittleEndian.Uint32(data[8:12]) == 0
- })).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, sessions)
- assert.NoError(t, h.sendV5UserOnline(recipient, onlineUIN, userStatus))
- })
- }
- func TestV5Handler_sendV5AckToAddr(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
- sender.EXPECT().SendPacket(testV5Addr, mock.MatchedBy(func(p []byte) bool {
- return v5ServerCommand(p) == ICQLegacySrvAck &&
- v5ServerSeq1(p) == testV5Seq1 &&
- v5ServerSeq2(p) == testV5Seq2 &&
- binary.LittleEndian.Uint32(p[3:7]) == testV5SessionID &&
- binary.LittleEndian.Uint32(p[13:17]) == testV5UIN &&
- len(v5ServerPacketData(p)) == 0
- })).Return(nil).Once()
- assert.NoError(t, h.sendV5AckToAddr(testV5Addr, testV5SessionID, testV5UIN, testV5Seq1, testV5Seq2))
- }
- func TestV5Handler_sendV5UserOffline(t *testing.T) {
- const offlineUIN = uint32(88888)
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- recipient := sessionWithInstance(testV5UIN)
- sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserOffline &&
- len(data) == 4 &&
- binary.LittleEndian.Uint32(data[0:4]) == offlineUIN
- })).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
- assert.NoError(t, h.sendV5UserOffline(recipient, offlineUIN))
- }
- func TestV5Handler_sendV5UserStatus(t *testing.T) {
- const (
- changedUIN = uint32(88888)
- status = uint32(0x00010002) // estat=1, status=2
- )
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- recipient := sessionWithInstance(testV5UIN)
- sender.EXPECT().SendToSession(recipient, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserStatus &&
- len(data) == 8 &&
- binary.LittleEndian.Uint32(data[0:4]) == changedUIN &&
- binary.LittleEndian.Uint16(data[4:6]) == uint16(status&0xFFFF) &&
- binary.LittleEndian.Uint16(data[6:8]) == uint16(status>>16)
- })).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, newTestV5SessionManager())
- assert.NoError(t, h.sendV5UserStatus(recipient, changedUIN, status))
- }
- func TestV5Handler_HandleLogoff_notifiesContacts(t *testing.T) {
- const contactUIN = uint32(88888)
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- loggingOff := sessionWithInstance(testV5UIN)
- loggingOff.SetContactList([]uint32{contactUIN})
- contact := sessionWithInstance(contactUIN)
- contact.SetContactList([]uint32{testV5UIN})
- registerSession(sessions, loggingOff)
- registerSession(sessions, contact)
- svc.EXPECT().NotifyUserOffline(mock.Anything, testV5UIN).Return(nil).Once()
- sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserOffline &&
- len(data) == 4 &&
- binary.LittleEndian.Uint32(data[0:4]) == testV5UIN
- })).Return(nil).Once()
- h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
- pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdLogoff, nil))
- assert.NoError(t, h.Handle(loggingOff, testV5Addr, pkt))
- assert.Nil(t, sessions.GetSession(testV5UIN))
- }
- func TestV5Handler_HandleSetStatus_notifiesContacts(t *testing.T) {
- const (
- contactUIN = uint32(88888)
- newStatus = uint32(0x00010002)
- )
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- changer := sessionWithInstance(testV5UIN)
- contact := sessionWithInstance(contactUIN)
- registerSession(sessions, changer)
- registerSession(sessions, contact)
- svc.EXPECT().ProcessStatusChange(mock.Anything, mock.Anything).
- Return(&StatusChangeResult{
- NotifyTargets: []NotifyTarget{{UIN: contactUIN}},
- }, nil).Once()
- sender.EXPECT().SendPacket(changer.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- sender.EXPECT().SendToSession(contact, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserStatus &&
- len(data) == 8 &&
- binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
- binary.LittleEndian.Uint16(data[4:6]) == uint16(newStatus&0xFFFF) &&
- binary.LittleEndian.Uint16(data[6:8]) == uint16(newStatus>>16)
- })).Return(nil).Once()
- h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
- pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdSetStatus, func() []byte {
- b := make([]byte, 4)
- binary.LittleEndian.PutUint32(b, newStatus)
- return b
- }()))
- assert.NoError(t, h.Handle(changer, testV5Addr, pkt))
- assert.Equal(t, newStatus, changer.GetStatus())
- }
- func TestV5Handler_HandleUserAdd_notifyTarget(t *testing.T) {
- const targetUIN = uint32(99999)
- userAddResult := &UserAddResult{
- TargetOnline: true,
- TargetStatus: 0x00000010,
- SendYouWereAdded: true,
- }
- t.Run("without dispatcher uses packet builder", func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- adder := sessionWithDirectConn(testV5UIN, 5191, 0x7F000002, 1)
- adder.SetStatus(0x00000020)
- target := sessionWithInstance(targetUIN)
- registerSession(sessions, adder)
- registerSession(sessions, target)
- svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
- Return(userAddResult, nil).Once()
- sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- sender.EXPECT().SendToSession(adder, mock.MatchedBy(func(p []byte) bool {
- return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
- len(v5ServerPacketData(p)) >= 4 &&
- binary.LittleEndian.Uint32(v5ServerPacketData(p)[0:4]) == targetUIN
- })).Return(nil).Once()
- sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
- if v5ServerCommand(p) != ICQLegacySrvSysMsgOnline {
- return false
- }
- data := v5ServerPacketData(p)
- return len(data) >= 8 &&
- binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
- binary.LittleEndian.Uint16(data[4:6]) == ICQLegacyMsgAdded
- })).Return(nil).Once()
- sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
- len(data) >= 4 &&
- binary.LittleEndian.Uint32(data[0:4]) == testV5UIN
- })).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, sessions)
- pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
- assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
- assert.Contains(t, adder.GetContactList(), targetUIN)
- })
- t.Run("with dispatcher sends user online via sendV5UserOnline", func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- adder := sessionWithDirectConn(testV5UIN, 5191, 0x7F000002, 1)
- adder.SetStatus(0x00000020)
- target := sessionWithInstance(targetUIN)
- registerSession(sessions, adder)
- registerSession(sessions, target)
- svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
- Return(userAddResult, nil).Once()
- sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- sender.EXPECT().SendToSession(adder, mock.MatchedBy(func(p []byte) bool {
- return v5ServerCommand(p) == ICQLegacySrvUserOnline
- })).Return(nil).Once()
- sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
- return v5ServerCommand(p) == ICQLegacySrvSysMsgOnline
- })).Return(nil).Once()
- sender.EXPECT().SendToSession(target, mock.MatchedBy(func(p []byte) bool {
- data := v5ServerPacketData(p)
- return v5ServerCommand(p) == ICQLegacySrvUserOnline &&
- len(data) >= 17 &&
- binary.LittleEndian.Uint32(data[0:4]) == testV5UIN &&
- binary.LittleEndian.Uint32(data[8:12]) == 5191 &&
- data[16] == 1
- })).Return(nil).Once()
- h := newTestV5HandlerWithDispatcher(t, sender, svc, sessions)
- pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
- assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
- })
- t.Run("SendYouWereAdded false skips target notifications", func(t *testing.T) {
- sender := newMockPacketSender(t)
- svc := newMockLegacyService(t)
- sessions := newTestV5SessionManager()
- adder := sessionWithInstance(testV5UIN)
- target := sessionWithInstance(targetUIN)
- registerSession(sessions, adder)
- registerSession(sessions, target)
- svc.EXPECT().ProcessUserAdd(mock.Anything, mock.Anything, mock.Anything).
- Return(&UserAddResult{
- TargetOnline: true,
- TargetStatus: 0x10,
- SendYouWereAdded: false,
- }, nil).Once()
- sender.EXPECT().SendPacket(adder.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- sender.EXPECT().SendToSession(adder, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- h := newTestV5Handler(t, sender, svc, sessions)
- pkt := buildEncryptedV5ClientPacket(t, defaultV5Pkt(ICQLegacyCmdUserAdd, v5TargetUINData(targetUIN)))
- assert.NoError(t, h.Handle(adder, testV5Addr, pkt))
- })
- }
- func TestV5Handler_HandleOfflineMsgReq(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "no messages",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
- Return([]LegacyOfflineMessage{}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSysMsgDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "with messages",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
- Return([]LegacyOfflineMessage{{FromUIN: 99999, ToUIN: testV5UIN, MsgType: 1, Message: "hi"}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3) // ack + msg + done
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgReq, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetOfflineMessages(mock.Anything, testV5UIN).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSysMsgDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleOfflineMsgAck(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "success",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().AckOfflineMessages(mock.Anything, testV5UIN).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSysMsgDoneAck, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().AckOfflineMessages(mock.Anything, testV5UIN).
- Return(errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "login info found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(testMinimalUser(), nil).Once()
- e.Service.EXPECT().GetUserInfo(mock.Anything, testV5UIN).
- Return(&LegacyUserSearchResult{UIN: testV5UIN}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
- },
- nil,
- ),
- },
- {
- name: "login info not found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2) // meta ack + fail
- },
- nil,
- ),
- },
- {
- name: "packet too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, []byte{0x01}),
- setup: v5CaseSetup(nil, func(e v5HandleEnv, err error) {
- require.Error(e.T, err)
- assert.Contains(e.T, err.Error(), "META_USER packet too short")
- }),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdMetaUser, v5MetaUserData(0x04CE, v5TargetUINData(testV5UIN))),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_Login(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "meta login reply",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaLogin, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "meta login empty subData",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaLogin, []byte{}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_GetInfo(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "login info alt opcode",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaLoginInfo2, v5TargetUINData(testV5UIN)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, testV5UIN).Return(testMinimalUser(), nil).Once()
- e.Service.EXPECT().GetUserInfo(mock.Anything, testV5UIN).
- Return(&LegacyUserSearchResult{UIN: testV5UIN}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
- },
- nil,
- ),
- },
- {
- name: "user full info found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserFullInfo, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
- },
- nil,
- ),
- },
- {
- name: "user full info data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserFullInfo, []byte{0x01, 0x02, 0x03}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "user full info2 found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserFullInfo2, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(8)
- },
- nil,
- ),
- },
- {
- name: "user full info2 not found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserFullInfo2, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "user info found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserInfo, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999, Nickname: "nick"}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "user info not found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserInfo, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_SetProfile(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "set basic success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetBasic2, v5MetaSetBasicData()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set basic service error",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetBasic2, v5MetaSetBasicData()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().UpdateBasicInfo(mock.Anything, testV5UIN, mock.Anything).
- Return(errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set work success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetWork2, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{
- Company: "co",
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().UpdateWorkInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set work parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetWork2, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set more success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetMore2, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo{
- Gender: 1,
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().UpdateMoreInfo(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set more parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetMore2, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set about success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetAbout, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{
- Notes: "about me",
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SetNotes(mock.Anything, testV5UIN, "about me").Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set about parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetAbout, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set interests success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetInterests, v5MetaInterestsWireData(t)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SetInterests(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set interests wrong count",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetInterests, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests{
- Interests: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{{Code: 1, Keyword: "only"}},
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set affiliations success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetAffiliations, v5MetaAffiliationsWireData(t)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SetAffiliations(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set affiliations wrong count",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetAffiliations, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
- PastAffiliations: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{{Code: 1, Keyword: "p1"}},
- Affiliations: []struct {
- Code uint16
- Keyword string `oscar:"len_prefix=uint16,nullterm"`
- }{{Code: 2, Keyword: "c1"}},
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set security success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetSecurity, v5MetaWireMarshal(t, wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions{
- Authorization: 0,
- WebAware: 1,
- })),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().UpdatePermissions(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set security parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetSecurity, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set password success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetPass, v5MetaSetPassData("newpass")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SetPassword(mock.Anything, testV5UIN, "", "newpass").Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set password truncated",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetPass, []byte{0x01}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set hpcat success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetHPCat, v5MetaSetHPCatData(true, 1, "home")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SetHomepageCategory(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "set hpcat data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSetHPCat, []byte{0x01, 0x02}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_Search(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "search name found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchName, v5MetaSearchNameData()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search name empty",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchName, v5MetaSearchNameData()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
- Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search uin found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchUIN, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfoForProtocol(mock.Anything, uint32(99999)).
- Return(&UserInfoResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search uin data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchUIN, []byte{0x01, 0x02, 0x03}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search email found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchEmail, v5MetaSearchEmailData("a@b.com")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search email empty",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchEmail, v5MetaSearchEmailData("a@b.com")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
- Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search white found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchWhite, v5LPString("alice")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().WhitePagesSearch(mock.Anything, mock.Anything).
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search white no criteria",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchWhite, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search white2 found",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchWhite2, v5LPString("alice")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "", "alice", "", "").
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "search white2 empty",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaSearchWhite2, v5LPString("alice")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "", "alice", "", "").
- Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_Unregister(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "unregister success",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserUnreg, v5MetaUnregisterData(testV5UIN, "secret")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().DeleteUser(mock.Anything, testV5UIN, "secret").Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "unregister data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(ICQLegacyMetaUserUnreg, []byte{0x01, 0x02, 0x03, 0x04, 0x05}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleMetaUser_Default(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "unknown sub-command",
- session: sessionWithInstance(testV5UIN),
- pkt: metaUserPkt(0xFFFF, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleVisibleList(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid list",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, v5ContactListData([]uint32{11111})),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.True(t, e.Session.IsOnVisibleList(11111))
- },
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, v5ContactListData([]uint32{11111})),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdVisibleList, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.False(t, e.Session.IsOnVisibleList(11111))
- },
- ),
- },
- })
- }
- func TestV5Handler_HandleInvisibleList(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid list",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, v5ContactListData([]uint32{22222})),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.True(t, e.Session.IsOnInvisibleList(22222))
- },
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, v5ContactListData([]uint32{22222})),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "parse error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInvisibleList, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- func(e v5HandleEnv, err error) {
- assert.NoError(e.T, err)
- assert.False(t, e.Session.IsOnInvisibleList(22222))
- },
- ),
- },
- })
- }
- func TestV5Handler_HandleChangeVILists(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "valid action",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, func() []byte {
- b := make([]byte, 5)
- b[0] = 1
- binary.LittleEndian.PutUint32(b[1:], 33333)
- return b
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, func() []byte {
- b := make([]byte, 5)
- b[0] = 1
- binary.LittleEndian.PutUint32(b[1:], 33333)
- return b
- }()),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- {
- name: "data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdChangeVILists, []byte{1}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleOldSearchUIN(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "user found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // found
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "user not found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, []byte{1, 2}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUIN, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleOldSearch(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "any data",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUser, []byte{0x01}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvSearchDone, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdSearchUser, []byte{0x01}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleOldInfoReq(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "user found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once() // ack
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvInfoReply, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "user not found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetUserInfo(mock.Anything, uint32(99999)).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, []byte{1, 2}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleOldExtInfoReq(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "user found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(testMinimalUser(), nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvExtInfoReply, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "user not found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "service error",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().GetFullUserInfo(mock.Anything, uint32(99999)).
- Return(nil, errors.New("fail")).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvInvalidUIN, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "data too short",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, []byte{1}),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdExtInfoReq, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleAckNewUIN(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "with session",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(ICQLegacyCmdRegNewUser, nil),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
- Run(func(_ *LegacySession, p []byte) {
- assert.Equal(t, ICQLegacySrvAck, v5ServerCommand(p))
- }).Return(nil).Once()
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(ICQLegacyCmdRegNewUser, nil),
- },
- })
- }
- func TestV5Handler_HandleDirectWhiteSearch(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "results found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0x0532, v5LPString("alice")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().WhitePagesSearch(mock.Anything, mock.Anything).
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2) // ack + result
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(0x0532, v5LPString("alice")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleDirectNameSearch(t *testing.T) {
- nameData := append(append(v5LPString("nick"), v5LPString("first")...), v5LPString("last")...)
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "results found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0x0514, nameData),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "nick", "first", "last", "").
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(0x0514, nameData),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleDirectUINSearch(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "user found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).
- Return(&LegacyUserSearchResult{UIN: 99999}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
- },
- nil,
- ),
- },
- {
- name: "user not found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByUIN(mock.Anything, uint32(99999)).Return(nil, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(2)
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(0x051F, v5TargetUINData(99999)),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
- func TestV5Handler_HandleDirectEmailSearch(t *testing.T) {
- runV5HandleCases(t, []v5HandleCase{
- {
- name: "results found",
- session: sessionWithInstance(testV5UIN),
- pkt: defaultV5Pkt(0x0528, v5LPString("a@b.com")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- e.Service.EXPECT().SearchByName(mock.Anything, "", "", "", "a@b.com").
- Return([]LegacyUserSearchResult{{UIN: 99999}}, nil).Once()
- e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Times(3)
- },
- nil,
- ),
- },
- {
- name: "nil session",
- pkt: defaultV5Pkt(0x0528, v5LPString("a@b.com")),
- setup: v5CaseSetup(
- func(e v5HandleEnv) {
- expectV5NotConnected(e.Sender)
- },
- nil,
- ),
- },
- })
- }
|