Kaynağa Gözat

thread ICQ SNAC request IDs and fix white-pages search replies

Echo the client's SNAC RequestID on ICQ DB replies (search, profile fetch,
offline mail, set-profile acks) so clients can correlate responses. Set
SNACFlagsMoreToCome on multi-part replies (full profile and non-final
search hits); clear it on the last fragment.

Fix search encoding: use distinct wire types for intermediate hits
(0x01A4 UserFound) and the final hit (0x01AE LastUserFound) instead of
mutating one struct via LastResult(). Add replySearchHit,
reportSearchFailure, and reportNoSearchResults helpers.

Plumb inFrame wire.SNACFrame through ICQService and the OSCAR handler;
regenerate mocks. Add wire.SNACFlagsMoreToCome. Extend FullUserInfo to
emit work info and notes in the multi-SNAC burst. Adjust populate_icq_users.sh
test data (ZIP field lengths, affiliation code).
Mike 2 ay önce
ebeveyn
işleme
531fbb2150

+ 306 - 377
foodgroup/icq.go

@@ -64,43 +64,21 @@ func (s *ICQService) DeleteMsgReq(ctx context.Context, instance *state.SessionIn
 	return nil
 }
 
-func (s *ICQService) FindByICQName(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error {
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		Success:    wire.ICQStatusCodeOK,
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-	}
-
+func (s *ICQService) FindByICQName(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error {
 	res, err := s.userFinder.FindByICQName(ctx, inBody.FirstName, inBody.LastName, inBody.NickName)
 
 	if err != nil {
 		s.logger.Error("FindByICQName failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	}
 	if len(res) == 0 {
-		resp.Success = wire.ICQStatusCodeFail
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	}
 
 	for i := 0; i < len(res); i++ {
-		if i == len(res)-1 {
-			resp.LastResult()
-		} else {
-			resp.ReqSubType = wire.ICQDBQueryMetaReplyUserFound
-		}
-		resp.Details = s.createResult(res[i])
-		if err := s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		}); err != nil {
+		last := i == len(res)-1
+		resp := s.createResult(res[i])
+		if err := s.replySearchHit(ctx, instance, resp, inFrame.RequestID, last, seq); err != nil {
 			return err
 		}
 	}
@@ -108,37 +86,20 @@ func (s *ICQService) FindByICQName(ctx context.Context, instance *state.SessionI
 	return nil
 }
 
-func (s *ICQService) FindByICQEmail(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error {
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-		Success:    wire.ICQStatusCodeOK,
-	}
-	resp.LastResult()
-
+func (s *ICQService) FindByICQEmail(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error {
 	res, err := s.userFinder.FindByICQEmail(ctx, inBody.Email)
 
 	switch {
 	case errors.Is(err, state.ErrNoUser):
-		resp.Success = wire.ICQStatusCodeFail
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	case err != nil:
-		s.logger.Error("FindByICQEmail failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	default:
-		resp.Success = wire.ICQStatusCodeOK
-		resp.Details = s.createResult(res)
+		return s.replySearchHit(ctx, instance, s.createResult(res), inFrame.RequestID, true, seq)
 	}
-
-	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-		Message: resp,
-	})
 }
 
-func (s *ICQService) FindByEmail3(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error {
+func (s *ICQService) FindByEmail3(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error {
 	b, hasEmail := inBody.Bytes(wire.ICQTLVTagsEmail)
 	if !hasEmail {
 		return errors.New("unable to get email from request")
@@ -148,74 +109,35 @@ func (s *ICQService) FindByEmail3(ctx context.Context, instance *state.SessionIn
 	if err := wire.UnmarshalLE(&email, bytes.NewReader(b)); err != nil {
 		return fmt.Errorf("unmarshal email: %w", err)
 	}
-
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-		Success:    wire.ICQStatusCodeOK,
-	}
-	resp.LastResult()
-
 	res, err := s.userFinder.FindByICQEmail(ctx, email.Email)
 
 	switch {
 	case errors.Is(err, state.ErrNoUser):
-		resp.Success = wire.ICQStatusCodeFail
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	case err != nil:
 		s.logger.Error("FindByICQEmail failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	default:
-		resp.Success = wire.ICQStatusCodeOK
-		resp.Details = s.createResult(res)
+		return s.replySearchHit(ctx, instance, s.createResult(res), inFrame.RequestID, true, seq)
 	}
-
-	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-		Message: resp,
-	})
 }
 
-func (s *ICQService) FindByICQInterests(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error {
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		Success:    wire.ICQStatusCodeOK,
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-	}
+func (s *ICQService) FindByICQInterests(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error {
 
 	interests := strings.Split(inBody.InterestsKeyword, ",")
 	res, err := s.userFinder.FindByICQInterests(ctx, inBody.InterestsCode, interests)
 
 	if err != nil {
-		s.logger.Error("FindByICQInterests failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	}
 	if len(res) == 0 {
-		resp.Success = wire.ICQStatusCodeFail
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	}
 
 	for i := 0; i < len(res); i++ {
-		if i == len(res)-1 {
-			resp.LastResult()
-		} else {
-			resp.ReqSubType = wire.ICQDBQueryMetaReplyUserFound
-		}
-		resp.Details = s.createResult(res[i])
-		if err := s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		}); err != nil {
+		last := i == len(res)-1
+		resp := s.createResult(res[i])
+		if err := s.replySearchHit(ctx, instance, resp, inFrame.RequestID, last, seq); err != nil {
 			return err
 		}
 	}
@@ -223,7 +145,7 @@ func (s *ICQService) FindByICQInterests(ctx context.Context, instance *state.Ses
 	return nil
 }
 
-func (s *ICQService) FindByWhitePages2(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error {
+func (s *ICQService) FindByWhitePages2(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error {
 	var criteria state.ICQUserSearchCriteria
 
 	if uin, ok := inBody.Uint32LE(wire.ICQTLVTagsUIN); ok {
@@ -350,27 +272,12 @@ func (s *ICQService) FindByWhitePages2(ctx context.Context, instance *state.Sess
 	}
 
 	users, err := s.userFinder.SearchICQUsers(ctx, criteria)
-
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		Success:    wire.ICQStatusCodeOK,
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-	}
-
 	if err != nil {
 		if errors.Is(err, state.ErrICQSearchEmptyCriteria) {
-			resp.Success = wire.ICQStatusCodeFail
-		} else {
-			s.logger.Error("FindByWhitePages2 failed", "err", err.Error())
-			resp.Success = wire.ICQStatusCodeErr
+			return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 		}
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		s.logger.Error("FindByWhitePages2 failed", "err", err.Error())
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	}
 
 	if onlineOnly {
@@ -384,22 +291,13 @@ func (s *ICQService) FindByWhitePages2(ctx context.Context, instance *state.Sess
 	}
 
 	if len(users) == 0 {
-		resp.Success = wire.ICQStatusCodeFail
-		return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		})
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	}
 
 	for i := 0; i < len(users); i++ {
-		if i == len(users)-1 {
-			resp.LastResult()
-		} else {
-			resp.ReqSubType = wire.ICQDBQueryMetaReplyUserFound
-		}
-		resp.Details = s.createResult(users[i])
-		if err := s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-			Message: resp,
-		}); err != nil {
+		isLast := i == len(users)-1
+		details := s.createResult(users[i])
+		if err := s.replySearchHit(ctx, instance, details, inFrame.RequestID, isLast, seq); err != nil {
 			return err
 		}
 	}
@@ -407,113 +305,81 @@ func (s *ICQService) FindByWhitePages2(ctx context.Context, instance *state.Sess
 	return nil
 }
 
-func (s *ICQService) FindByUIN(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-		Success:    wire.ICQStatusCodeOK,
-	}
-	resp.LastResult()
-
+func (s *ICQService) FindByUIN(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
 	res, err := s.userFinder.FindByUIN(ctx, inBody.UIN)
 
 	switch {
 	case errors.Is(err, state.ErrNoUser):
-		resp.Success = wire.ICQStatusCodeFail
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	case err != nil:
 		s.logger.Error("FindByUIN failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	default:
-		resp.Success = wire.ICQStatusCodeOK
-		resp.Details = s.createResult(res)
+		return s.replySearchHit(ctx, instance, s.createResult(res), inFrame.RequestID, true, seq)
 	}
-
-	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-		Message: resp,
-	})
 }
 
-func (s *ICQService) FindByUIN2(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error {
+func (s *ICQService) FindByUIN2(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error {
 	UIN, hasUIN := inBody.Uint32LE(wire.ICQTLVTagsUIN)
 	if !hasUIN {
 		return errors.New("unable to get UIN from request")
 	}
 
-	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
-		ICQMetadata: wire.ICQMetadata{
-			UIN:     instance.UIN(),
-			ReqType: wire.ICQDBQueryMetaReply,
-			Seq:     seq,
-		},
-		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
-		Success:    wire.ICQStatusCodeOK,
-	}
-	resp.LastResult()
-
 	res, err := s.userFinder.FindByUIN(ctx, UIN)
 
 	switch {
 	case errors.Is(err, state.ErrNoUser):
-		resp.Success = wire.ICQStatusCodeFail
+		return s.reportNoSearchResults(ctx, instance, seq, inFrame.RequestID)
 	case err != nil:
 		s.logger.Error("FindByUIN failed", "err", err.Error())
-		resp.Success = wire.ICQStatusCodeErr
+		return s.reportSearchFailure(ctx, instance, seq, inFrame.RequestID)
 	default:
-		resp.Success = wire.ICQStatusCodeOK
-		resp.Details = s.createResult(res)
+		return s.replySearchHit(ctx, instance, s.createResult(res), inFrame.RequestID, true, seq)
 	}
-
-	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
-		Message: resp,
-	})
 }
 
-func (s *ICQService) FullUserInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
+func (s *ICQService) FullUserInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
 
 	user, err := s.userFinder.FindByUIN(ctx, inBody.UIN)
 	if err != nil {
 		return err
 	}
 
-	if err := s.userInfo(ctx, instance, user, seq); err != nil {
+	if err := s.userInfo(ctx, instance, user, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.moreUserInfo(ctx, instance, user, seq); err != nil {
+	if err := s.moreUserInfo(ctx, instance, user, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.extraEmails(ctx, instance, user, seq); err != nil {
+	if err := s.extraEmails(ctx, instance, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.homepageCat(ctx, instance, user, seq); err != nil {
+	if err := s.homepageCat(ctx, instance, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.workInfo(ctx, instance, user, seq); err != nil {
+	if err := s.workInfo(ctx, instance, user, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.notes(ctx, instance, user, seq); err != nil {
+	if err := s.notes(ctx, instance, user, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.interests(ctx, instance, user, seq); err != nil {
+	if err := s.interests(ctx, instance, user, inFrame.RequestID, seq, wire.SNACFlagsMoreToCome); err != nil {
 		return err
 	}
 
-	if err := s.affiliations(ctx, instance, user, seq); err != nil {
+	if err := s.affiliations(ctx, instance, user, inFrame.RequestID, seq, 0); err != nil {
 		return err
 	}
 	return nil
 }
 
-func (s *ICQService) OfflineMsgReq(ctx context.Context, instance *state.SessionInstance, seq uint16) error {
+func (s *ICQService) OfflineMsgReq(ctx context.Context, inFrame wire.SNACFrame, instance *state.SessionInstance, seq uint16) error {
 	messages, err := s.offlineMessageManager.RetrieveMessages(ctx, instance.IdentScreenName())
 	if err != nil {
 		return fmt.Errorf("retrieving messages: %w", err)
@@ -579,7 +445,7 @@ func (s *ICQService) OfflineMsgReq(ctx context.Context, instance *state.SessionI
 		msgOut := wire.ICQMessageReplyEnvelope{
 			Message: reply,
 		}
-		if err := s.reply(ctx, instance, msgOut); err != nil {
+		if err := s.reply(ctx, instance, msgOut, inFrame.RequestID, 0); err != nil {
 			return fmt.Errorf("sending offline message: %w", err)
 		}
 	}
@@ -595,14 +461,14 @@ func (s *ICQService) OfflineMsgReq(ctx context.Context, instance *state.SessionI
 		},
 	}
 
-	if err := s.reply(ctx, instance, eofMsg); err != nil {
+	if err := s.reply(ctx, instance, eofMsg, inFrame.RequestID, 0); err != nil {
 		return fmt.Errorf("sending end of offline messages: %w", err)
 	}
 
 	return nil
 }
 
-func (s *ICQService) SetAffiliations(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error {
+func (s *ICQService) SetAffiliations(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error {
 	if len(inBody.PastAffiliations) != 3 || len(inBody.Affiliations) != 3 {
 		return fmt.Errorf("%w: expected 3 past affiliations and 3 affiliations", errICQBadRequest)
 	}
@@ -625,10 +491,10 @@ func (s *ICQService) SetAffiliations(ctx context.Context, instance *state.Sessio
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetAffiliations)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetAffiliations, inFrame.RequestID)
 }
 
-func (s *ICQService) SetBasicInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo, seq uint16) error {
+func (s *ICQService) SetBasicInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo, seq uint16) error {
 	u := state.ICQBasicInfo{
 		CellPhone:    inBody.CellPhone,
 		CountryCode:  inBody.CountryCode,
@@ -650,14 +516,14 @@ func (s *ICQService) SetBasicInfo(ctx context.Context, instance *state.SessionIn
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetBasicInfo)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetBasicInfo, inFrame.RequestID)
 }
 
-func (s *ICQService) SetEmails(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails, seq uint16) error {
+func (s *ICQService) SetEmails(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails, seq uint16) error {
 	if len(inBody.Emails) > 0 {
 		s.logger.Debug("adding additional emails is not yet supported")
 	}
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetEmails)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetEmails, inFrame.RequestID)
 }
 
 // SetICQInfo handles the TLV-based CLI_SET_FULLINFO (0x0C3A) request used to
@@ -667,7 +533,7 @@ func (s *ICQService) SetEmails(ctx context.Context, instance *state.SessionInsta
 // multi-valued group (languages, interests, affiliations) any unspecified
 // slots are reset when at least one TLV in that group is present, matching
 // the way ICQLite tabs replace their entire group on save.
-func (s *ICQService) SetICQInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo, seq uint16) error {
+func (s *ICQService) SetICQInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo, seq uint16) error {
 	user, err := s.userFinder.FindByUIN(ctx, instance.UIN())
 	if err != nil {
 		return fmt.Errorf("FindByUIN: %w", err)
@@ -904,15 +770,15 @@ func (s *ICQService) SetICQInfo(ctx context.Context, instance *state.SessionInst
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetFullInfo)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetFullInfo, inFrame.RequestID)
 }
 
-func (s *ICQService) SetICQPhone(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone, seq uint16) error {
+func (s *ICQService) SetICQPhone(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone, seq uint16) error {
 	s.logger.Debug("received SetICQPhone request")
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetICQPhone)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetICQPhone, inFrame.RequestID)
 }
 
-func (s *ICQService) SetInterests(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests, seq uint16) error {
+func (s *ICQService) SetInterests(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests, seq uint16) error {
 	if len(inBody.Interests) != 4 {
 		return fmt.Errorf("%w: expected 4 interests", errICQBadRequest)
 	}
@@ -931,10 +797,10 @@ func (s *ICQService) SetInterests(ctx context.Context, instance *state.SessionIn
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetInterests)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetInterests, inFrame.RequestID)
 }
 
-func (s *ICQService) SetMoreInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo, seq uint16) error {
+func (s *ICQService) SetMoreInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo, seq uint16) error {
 	u := state.ICQMoreInfo{
 		Gender:       inBody.Gender,
 		HomePageAddr: inBody.HomePageAddr,
@@ -950,13 +816,13 @@ func (s *ICQService) SetMoreInfo(ctx context.Context, instance *state.SessionIns
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetMoreInfo)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetMoreInfo, inFrame.RequestID)
 }
 
 // SetPermissions persists ICQ privacy flags. AuthRequired controls whether other
 // users need authorization to add this user; when true, contact pre-authorization
 // can allow specific users to add without a new prompt.
-func (s *ICQService) SetPermissions(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions, seq uint16) error {
+func (s *ICQService) SetPermissions(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions, seq uint16) error {
 	u := state.ICQPermissions{
 		AuthRequired: inBody.Authorization == 0,
 		WebAware:     inBody.WebAware == 1,
@@ -966,10 +832,10 @@ func (s *ICQService) SetPermissions(ctx context.Context, instance *state.Session
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetPermissions)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetPermissions, inFrame.RequestID)
 }
 
-func (s *ICQService) SetUserNotes(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes, seq uint16) error {
+func (s *ICQService) SetUserNotes(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes, seq uint16) error {
 	u := state.ICQUserNotes{
 		Notes: inBody.Notes,
 	}
@@ -978,10 +844,10 @@ func (s *ICQService) SetUserNotes(ctx context.Context, instance *state.SessionIn
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetNotes)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetNotes, inFrame.RequestID)
 }
 
-func (s *ICQService) SetWorkInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo, seq uint16) error {
+func (s *ICQService) SetWorkInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo, seq uint16) error {
 	icqWorkInfo := state.ICQWorkInfo{
 		Company:        inBody.Company,
 		Department:     inBody.Department,
@@ -1001,10 +867,10 @@ func (s *ICQService) SetWorkInfo(ctx context.Context, instance *state.SessionIns
 		return err
 	}
 
-	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetWorkInfo)
+	return s.reqAck(ctx, instance, seq, wire.ICQDBQueryMetaReplySetWorkInfo, inFrame.RequestID)
 }
 
-func (s *ICQService) ShortUserInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo, seq uint16) error {
+func (s *ICQService) ShortUserInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo, seq uint16) error {
 	user, err := s.userFinder.FindByUIN(ctx, inBody.UIN)
 	if err != nil {
 		if errors.Is(err, state.ErrNoUser) {
@@ -1019,7 +885,7 @@ func (s *ICQService) ShortUserInfo(ctx context.Context, instance *state.SessionI
 					Success:    wire.ICQStatusCodeErr,
 				},
 			}
-			return s.reply(ctx, instance, msg)
+			return s.reply(ctx, instance, msg, inFrame.RequestID, 0)
 		}
 		return err
 	}
@@ -1046,10 +912,10 @@ func (s *ICQService) ShortUserInfo(ctx context.Context, instance *state.SessionI
 		Message: info,
 	}
 
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, inFrame.RequestID, 0)
 }
 
-func (s *ICQService) XMLReqData(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq, seq uint16) error {
+func (s *ICQService) XMLReqData(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq, seq uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
 		Message: wire.ICQ_0x07DA_0x08A2_DBQueryMetaReplyXMLData{
 			ICQMetadata: wire.ICQMetadata{
@@ -1061,85 +927,88 @@ func (s *ICQService) XMLReqData(ctx context.Context, instance *state.SessionInst
 			Success:    wire.ICQStatusCodeErr,
 		},
 	}
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, inFrame.RequestID, 0)
+}
+
+func (s *ICQService) userInfo(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
+	userInfo := wire.ICQ_0x07DA_0x00C8_DBQueryMetaReplyBasicInfo{
+		ICQMetadata: wire.ICQMetadata{
+			UIN:     instance.UIN(),
+			ReqType: wire.ICQDBQueryMetaReply,
+			Seq:     seq,
+		},
+		ReqSubType:  wire.ICQDBQueryMetaReplyBasicInfo,
+		Success:     wire.ICQStatusCodeOK,
+		Nickname:    user.ICQInfo.Basic.Nickname,
+		FirstName:   user.ICQInfo.Basic.FirstName,
+		LastName:    user.ICQInfo.Basic.LastName,
+		Email:       user.ICQInfo.Basic.EmailAddress,
+		City:        user.ICQInfo.Basic.City,
+		State:       user.ICQInfo.Basic.State,
+		Phone:       user.ICQInfo.Basic.Phone,
+		Fax:         user.ICQInfo.Basic.Fax,
+		Address:     user.ICQInfo.Basic.Address,
+		CellPhone:   user.ICQInfo.Basic.CellPhone,
+		ZIP:         user.ICQInfo.Basic.ZIPCode,
+		CountryCode: user.ICQInfo.Basic.CountryCode,
+		GMTOffset:   user.ICQInfo.Basic.GMTOffset,
+		AuthFlag:    0, // required by default
+		WebAware:    1,
+		DCPerms:     0,
+	}
+
+	if !user.ICQInfo.Permissions.AuthRequired {
+		userInfo.AuthFlag = 1
+	}
+	if user.ICQInfo.Permissions.WebAware {
+		userInfo.WebAware = 1
+	} else {
+		userInfo.WebAware = 0
+	}
+
+	if user.ICQInfo.Basic.PublishEmail {
+		userInfo.PublishEmail = wire.ICQUserFlagPublishEmailYes
+	} else {
+		userInfo.PublishEmail = wire.ICQUserFlagPublishEmailNo
+	}
+
+	msg := wire.ICQMessageReplyEnvelope{
+		Message: userInfo,
+	}
+	return s.reply(ctx, instance, msg, requestID, flags)
+
 }
 
-func (s *ICQService) affiliations(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) moreUserInfo(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
-		Message: wire.ICQ_0x07DA_0x00FA_DBQueryMetaReplyAffiliations{
+		Message: wire.ICQ_0x07DA_0x00DC_DBQueryMetaReplyMoreInfo{
 			ICQMetadata: wire.ICQMetadata{
 				UIN:     instance.UIN(),
 				ReqType: wire.ICQDBQueryMetaReply,
 				Seq:     seq,
 			},
-			ReqSubType: wire.ICQDBQueryMetaReplyAffiliations,
-			Success:    wire.ICQStatusCodeOK,
-			ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations: wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
-				PastAffiliations: []struct {
-					Code    uint16
-					Keyword string `oscar:"len_prefix=uint16,nullterm"`
-				}{
-					{
-						Code:    user.ICQInfo.Affiliations.PastCode1,
-						Keyword: user.ICQInfo.Affiliations.PastKeyword1,
-					},
-					{
-						Code:    user.ICQInfo.Affiliations.PastCode2,
-						Keyword: user.ICQInfo.Affiliations.PastKeyword2,
-					},
-					{
-						Code:    user.ICQInfo.Affiliations.PastCode3,
-						Keyword: user.ICQInfo.Affiliations.PastKeyword3,
-					},
-				},
-				Affiliations: []struct {
-					Code    uint16
-					Keyword string `oscar:"len_prefix=uint16,nullterm"`
-				}{
-					{
-						Code:    user.ICQInfo.Affiliations.CurrentCode1,
-						Keyword: user.ICQInfo.Affiliations.CurrentKeyword1,
-					},
-					{
-						Code:    user.ICQInfo.Affiliations.CurrentCode2,
-						Keyword: user.ICQInfo.Affiliations.CurrentKeyword2,
-					},
-					{
-						Code:    user.ICQInfo.Affiliations.CurrentCode3,
-						Keyword: user.ICQInfo.Affiliations.CurrentKeyword3,
-					},
-				},
-			},
+			ReqSubType:   wire.ICQDBQueryMetaReplyMoreInfo,
+			Success:      wire.ICQStatusCodeOK,
+			Age:          uint16(user.Age(s.timeNow)),
+			Gender:       uint8(user.ICQInfo.More.Gender),
+			HomePageAddr: user.ICQInfo.More.HomePageAddr,
+			BirthYear:    user.ICQInfo.More.BirthYear,
+			BirthMonth:   user.ICQInfo.More.BirthMonth,
+			BirthDay:     user.ICQInfo.More.BirthDay,
+			Lang1:        user.ICQInfo.More.Lang1,
+			Lang2:        user.ICQInfo.More.Lang2,
+			Lang3:        user.ICQInfo.More.Lang3,
+			City:         user.ICQInfo.Basic.City,
+			State:        user.ICQInfo.Basic.State,
+			CountryCode:  user.ICQInfo.Basic.CountryCode,
+			TimeZone:     user.ICQInfo.Basic.GMTOffset,
 		},
 	}
 
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, requestID, flags)
 }
 
-func (s *ICQService) createResult(res state.User) wire.ICQUserSearchRecord {
-	uin, _ := strconv.Atoi(res.IdentScreenName.String())
-
-	searchRecord := wire.ICQUserSearchRecord{
-		UIN:       uint32(uin),
-		Nickname:  res.ICQInfo.Basic.Nickname,
-		FirstName: res.ICQInfo.Basic.FirstName,
-		LastName:  res.ICQInfo.Basic.LastName,
-		Email:     res.ICQInfo.Basic.EmailAddress,
-		Gender:    uint8(res.ICQInfo.More.Gender),
-		Age:       res.Age(s.timeNow),
-	}
-	if !res.ICQInfo.Permissions.AuthRequired {
-		searchRecord.Authorization = 1
-	}
-
-	userSess := s.sessionRetriever.RetrieveSession(res.IdentScreenName)
-	if userSess != nil {
-		searchRecord.OnlineStatus = 1
-	}
-	return searchRecord
-}
-
-func (s *ICQService) extraEmails(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) extraEmails(ctx context.Context, instance *state.SessionInstance, requestID uint32, seq uint16, flags uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
 		Message: wire.ICQ_0x07DA_0x00EB_DBQueryMetaReplyExtEmailInfo{
 			ICQMetadata: wire.ICQMetadata{
@@ -1152,10 +1021,10 @@ func (s *ICQService) extraEmails(ctx context.Context, instance *state.SessionIns
 		},
 	}
 
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, requestID, flags)
 }
 
-func (s *ICQService) homepageCat(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) homepageCat(ctx context.Context, instance *state.SessionInstance, requestID uint32, seq uint16, flags uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
 		Message: wire.ICQ_0x07DA_0x010E_DBQueryMetaReplyHomePageCat{
 			ICQMetadata: wire.ICQMetadata{
@@ -1168,10 +1037,58 @@ func (s *ICQService) homepageCat(ctx context.Context, instance *state.SessionIns
 		},
 	}
 
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, requestID, flags)
 }
 
-func (s *ICQService) interests(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) workInfo(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
+	msg := wire.ICQMessageReplyEnvelope{
+		Message: wire.ICQ_0x07DA_0x00D2_DBQueryMetaReplyWorkInfo{
+			ICQMetadata: wire.ICQMetadata{
+				UIN:     instance.UIN(),
+				ReqType: wire.ICQDBQueryMetaReply,
+				Seq:     seq,
+			},
+			ReqSubType: wire.ICQDBQueryMetaReplyWorkInfo,
+			Success:    wire.ICQStatusCodeOK,
+			ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo: wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{
+				City:           user.ICQInfo.Work.City,
+				State:          user.ICQInfo.Work.State,
+				Phone:          user.ICQInfo.Work.Phone,
+				Fax:            user.ICQInfo.Work.Fax,
+				Address:        user.ICQInfo.Work.Address,
+				ZIP:            user.ICQInfo.Work.ZIPCode,
+				CountryCode:    user.ICQInfo.Work.CountryCode,
+				Company:        user.ICQInfo.Work.Company,
+				Department:     user.ICQInfo.Work.Department,
+				Position:       user.ICQInfo.Work.Position,
+				OccupationCode: user.ICQInfo.Work.OccupationCode,
+				WebPage:        user.ICQInfo.Work.WebPage,
+			},
+		},
+	}
+	return s.reply(ctx, instance, msg, requestID, flags)
+}
+
+func (s *ICQService) notes(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
+	msg := wire.ICQMessageReplyEnvelope{
+		Message: wire.ICQ_0x07DA_0x00E6_DBQueryMetaReplyNotes{
+			ICQMetadata: wire.ICQMetadata{
+				UIN:     instance.UIN(),
+				ReqType: wire.ICQDBQueryMetaReply,
+				Seq:     seq,
+			},
+			ReqSubType: wire.ICQDBQueryMetaReplyNotes,
+			Success:    wire.ICQStatusCodeOK,
+			ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes: wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{
+				Notes: user.ICQInfo.Notes.Notes,
+			},
+		},
+	}
+
+	return s.reply(ctx, instance, msg, requestID, flags)
+}
+
+func (s *ICQService) interests(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
 		Message: wire.ICQ_0x07DA_0x00F0_DBQueryMetaReplyInterests{
 			ICQMetadata: wire.ICQMetadata{
@@ -1205,62 +1122,68 @@ func (s *ICQService) interests(ctx context.Context, instance *state.SessionInsta
 		},
 	}
 
-	return s.reply(ctx, instance, msg)
-}
-
-func (s *ICQService) moreUserInfo(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
-	msg := wire.ICQMessageReplyEnvelope{
-		Message: wire.ICQ_0x07DA_0x00DC_DBQueryMetaReplyMoreInfo{
-			ICQMetadata: wire.ICQMetadata{
-				UIN:     instance.UIN(),
-				ReqType: wire.ICQDBQueryMetaReply,
-				Seq:     seq,
-			},
-			ReqSubType:   wire.ICQDBQueryMetaReplyMoreInfo,
-			Success:      wire.ICQStatusCodeOK,
-			Age:          uint16(user.Age(s.timeNow)),
-			Gender:       uint8(user.ICQInfo.More.Gender),
-			HomePageAddr: user.ICQInfo.More.HomePageAddr,
-			BirthYear:    user.ICQInfo.More.BirthYear,
-			BirthMonth:   user.ICQInfo.More.BirthMonth,
-			BirthDay:     user.ICQInfo.More.BirthDay,
-			Lang1:        user.ICQInfo.More.Lang1,
-			Lang2:        user.ICQInfo.More.Lang2,
-			Lang3:        user.ICQInfo.More.Lang3,
-			City:         user.ICQInfo.Basic.City,
-			State:        user.ICQInfo.Basic.State,
-			CountryCode:  user.ICQInfo.Basic.CountryCode,
-			TimeZone:     user.ICQInfo.Basic.GMTOffset,
-		},
-	}
-
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, requestID, flags)
 }
 
-func (s *ICQService) notes(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) affiliations(ctx context.Context, instance *state.SessionInstance, user state.User, requestID uint32, seq uint16, flags uint16) error {
 	msg := wire.ICQMessageReplyEnvelope{
-		Message: wire.ICQ_0x07DA_0x00E6_DBQueryMetaReplyNotes{
+		Message: wire.ICQ_0x07DA_0x00FA_DBQueryMetaReplyAffiliations{
 			ICQMetadata: wire.ICQMetadata{
 				UIN:     instance.UIN(),
 				ReqType: wire.ICQDBQueryMetaReply,
 				Seq:     seq,
 			},
-			ReqSubType: wire.ICQDBQueryMetaReplyNotes,
+			ReqSubType: wire.ICQDBQueryMetaReplyAffiliations,
 			Success:    wire.ICQStatusCodeOK,
-			ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes: wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes{
-				Notes: user.ICQInfo.Notes.Notes,
+			ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations: wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations{
+				PastAffiliations: []struct {
+					Code    uint16
+					Keyword string `oscar:"len_prefix=uint16,nullterm"`
+				}{
+					{
+						Code:    user.ICQInfo.Affiliations.PastCode1,
+						Keyword: user.ICQInfo.Affiliations.PastKeyword1,
+					},
+					{
+						Code:    user.ICQInfo.Affiliations.PastCode2,
+						Keyword: user.ICQInfo.Affiliations.PastKeyword2,
+					},
+					{
+						Code:    user.ICQInfo.Affiliations.PastCode3,
+						Keyword: user.ICQInfo.Affiliations.PastKeyword3,
+					},
+				},
+				Affiliations: []struct {
+					Code    uint16
+					Keyword string `oscar:"len_prefix=uint16,nullterm"`
+				}{
+					{
+						Code:    user.ICQInfo.Affiliations.CurrentCode1,
+						Keyword: user.ICQInfo.Affiliations.CurrentKeyword1,
+					},
+					{
+						Code:    user.ICQInfo.Affiliations.CurrentCode2,
+						Keyword: user.ICQInfo.Affiliations.CurrentKeyword2,
+					},
+					{
+						Code:    user.ICQInfo.Affiliations.CurrentCode3,
+						Keyword: user.ICQInfo.Affiliations.CurrentKeyword3,
+					},
+				},
 			},
 		},
 	}
 
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, msg, requestID, flags)
 }
 
-func (s *ICQService) reply(ctx context.Context, instance *state.SessionInstance, message wire.ICQMessageReplyEnvelope) error {
+func (s *ICQService) reply(ctx context.Context, instance *state.SessionInstance, message wire.ICQMessageReplyEnvelope, requestID uint32, snacFlags uint16) error {
 	msg := wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.ICQ,
 			SubGroup:  wire.ICQDBReply,
+			RequestID: requestID,
+			Flags:     snacFlags,
 		},
 		Body: wire.SNAC_0x15_0x02_DBReply{
 			TLVRestBlock: wire.TLVRestBlock{
@@ -1275,96 +1198,102 @@ func (s *ICQService) reply(ctx context.Context, instance *state.SessionInstance,
 	return nil
 }
 
-func (s *ICQService) reqAck(ctx context.Context, instance *state.SessionInstance, seq uint16, subType uint16) error {
-	msg := wire.ICQMessageReplyEnvelope{
-		Message: wire.ICQ_0x07DA_0x00DC_DBQueryMetaReplyMoreInfo{
+func (s *ICQService) replySearchHit(ctx context.Context, instance *state.SessionInstance, record wire.ICQUserSearchRecord, requestID uint32, last bool, seq uint16) error {
+	var resp any
+	var more uint16
+	if last {
+		resp = wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
 			ICQMetadata: wire.ICQMetadata{
 				UIN:     instance.UIN(),
 				ReqType: wire.ICQDBQueryMetaReply,
 				Seq:     seq,
 			},
-			ReqSubType: subType,
+			ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
 			Success:    wire.ICQStatusCodeOK,
-		},
+			Details:    record,
+			UsersLeft:  0,
+		}
+	} else {
+		resp = wire.ICQ_0x07DA_0x01A4_DBQueryMetaReplyUserFound{
+			ICQMetadata: wire.ICQMetadata{
+				UIN:     instance.UIN(),
+				ReqType: wire.ICQDBQueryMetaReply,
+				Seq:     seq,
+			},
+			ReqSubType: wire.ICQDBQueryMetaReplyUserFound,
+			Success:    wire.ICQStatusCodeOK,
+			Details:    record,
+		}
+		more = wire.SNACFlagsMoreToCome
 	}
-
-	return s.reply(ctx, instance, msg)
+	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{Message: resp}, requestID, more)
 }
 
-func (s *ICQService) userInfo(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
-	userInfo := wire.ICQ_0x07DA_0x00C8_DBQueryMetaReplyBasicInfo{
+func (s *ICQService) reportSearchFailure(ctx context.Context, instance *state.SessionInstance, seq uint16, requestID uint32) error {
+	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
 		ICQMetadata: wire.ICQMetadata{
 			UIN:     instance.UIN(),
 			ReqType: wire.ICQDBQueryMetaReply,
 			Seq:     seq,
 		},
-		ReqSubType:  wire.ICQDBQueryMetaReplyBasicInfo,
-		Success:     wire.ICQStatusCodeOK,
-		Nickname:    user.ICQInfo.Basic.Nickname,
-		FirstName:   user.ICQInfo.Basic.FirstName,
-		LastName:    user.ICQInfo.Basic.LastName,
-		Email:       user.ICQInfo.Basic.EmailAddress,
-		City:        user.ICQInfo.Basic.City,
-		State:       user.ICQInfo.Basic.State,
-		Phone:       user.ICQInfo.Basic.Phone,
-		Fax:         user.ICQInfo.Basic.Fax,
-		Address:     user.ICQInfo.Basic.Address,
-		CellPhone:   user.ICQInfo.Basic.CellPhone,
-		ZIP:         user.ICQInfo.Basic.ZIPCode,
-		CountryCode: user.ICQInfo.Basic.CountryCode,
-		GMTOffset:   user.ICQInfo.Basic.GMTOffset,
-		AuthFlag:    0, // required by default
-		WebAware:    1,
-		DCPerms:     0,
-	}
-
-	if !user.ICQInfo.Permissions.AuthRequired {
-		userInfo.AuthFlag = 1
-	}
-	if user.ICQInfo.Permissions.WebAware {
-		userInfo.WebAware = 1
-	} else {
-		userInfo.WebAware = 0
-	}
-
-	if user.ICQInfo.Basic.PublishEmail {
-		userInfo.PublishEmail = wire.ICQUserFlagPublishEmailYes
-	} else {
-		userInfo.PublishEmail = wire.ICQUserFlagPublishEmailNo
+		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
+		Success:    wire.ICQStatusCodeErr,
 	}
+	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
+		Message: resp,
+	}, requestID, 0)
+}
 
-	msg := wire.ICQMessageReplyEnvelope{
-		Message: userInfo,
+func (s *ICQService) reportNoSearchResults(ctx context.Context, instance *state.SessionInstance, seq uint16, requestID uint32) error {
+	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+		ICQMetadata: wire.ICQMetadata{
+			UIN:     instance.UIN(),
+			ReqType: wire.ICQDBQueryMetaReply,
+			Seq:     seq,
+		},
+		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
+		Success:    wire.ICQStatusCodeFail,
 	}
-	return s.reply(ctx, instance, msg)
-
+	return s.reply(ctx, instance, wire.ICQMessageReplyEnvelope{
+		Message: resp,
+	}, requestID, 0)
 }
 
-func (s *ICQService) workInfo(ctx context.Context, instance *state.SessionInstance, user state.User, seq uint16) error {
+func (s *ICQService) reqAck(ctx context.Context, instance *state.SessionInstance, seq uint16, subType uint16, requestID uint32) error {
 	msg := wire.ICQMessageReplyEnvelope{
-		Message: wire.ICQ_0x07DA_0x00D2_DBQueryMetaReplyWorkInfo{
+		Message: wire.ICQ_0x07DA_0x00DC_DBQueryMetaReplyMoreInfo{
 			ICQMetadata: wire.ICQMetadata{
 				UIN:     instance.UIN(),
 				ReqType: wire.ICQDBQueryMetaReply,
 				Seq:     seq,
 			},
-			ReqSubType: wire.ICQDBQueryMetaReplyWorkInfo,
+			ReqSubType: subType,
 			Success:    wire.ICQStatusCodeOK,
-			ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo: wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo{
-				City:           user.ICQInfo.Work.City,
-				State:          user.ICQInfo.Work.State,
-				Phone:          user.ICQInfo.Work.Phone,
-				Fax:            user.ICQInfo.Work.Fax,
-				Address:        user.ICQInfo.Work.Address,
-				ZIP:            user.ICQInfo.Work.ZIPCode,
-				CountryCode:    user.ICQInfo.Work.CountryCode,
-				Company:        user.ICQInfo.Work.Company,
-				Department:     user.ICQInfo.Work.Department,
-				Position:       user.ICQInfo.Work.Position,
-				OccupationCode: user.ICQInfo.Work.OccupationCode,
-				WebPage:        user.ICQInfo.Work.WebPage,
-			},
 		},
 	}
-	return s.reply(ctx, instance, msg)
+
+	return s.reply(ctx, instance, msg, requestID, 0)
+}
+
+func (s *ICQService) createResult(res state.User) wire.ICQUserSearchRecord {
+	uin, _ := strconv.Atoi(res.IdentScreenName.String())
+
+	searchRecord := wire.ICQUserSearchRecord{
+		UIN:       uint32(uin),
+		Nickname:  res.ICQInfo.Basic.Nickname,
+		FirstName: res.ICQInfo.Basic.FirstName,
+		LastName:  res.ICQInfo.Basic.LastName,
+		Email:     res.ICQInfo.Basic.EmailAddress,
+		Gender:    uint8(res.ICQInfo.More.Gender),
+		Age:       res.Age(s.timeNow),
+	}
+	if !res.ICQInfo.Permissions.AuthRequired {
+		searchRecord.Authorization = 1
+	}
+
+	userSess := s.sessionRetriever.RetrieveSession(res.IdentScreenName)
+	if userSess != nil {
+		searchRecord.OnlineStatus = 1
+	}
+	return searchRecord
 }

+ 75 - 74
foodgroup/icq_test.go

@@ -136,12 +136,14 @@ func TestICQService_FindByICQName(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
 											wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
-												Message: wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+												Message: wire.ICQ_0x07DA_0x01A4_DBQueryMetaReplyUserFound{
 													ICQMetadata: wire.ICQMetadata{
 														UIN:     11111111,
 														ReqType: wire.ICQDBQueryMetaReply,
@@ -173,6 +175,7 @@ func TestICQService_FindByICQName(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -197,11 +200,6 @@ func TestICQService_FindByICQName(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -254,7 +252,7 @@ func TestICQService_FindByICQName(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByICQName(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByICQName(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -316,6 +314,7 @@ func TestICQService_FindByICQEmail(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -340,11 +339,6 @@ func TestICQService_FindByICQEmail(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -393,7 +387,7 @@ func TestICQService_FindByICQEmail(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByICQEmail(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByICQEmail(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -461,6 +455,7 @@ func TestICQService_FindByEmail3(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -485,11 +480,6 @@ func TestICQService_FindByEmail3(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -538,7 +528,7 @@ func TestICQService_FindByEmail3(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByEmail3(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByEmail3(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -600,6 +590,7 @@ func TestICQService_FindByUIN(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -624,11 +615,6 @@ func TestICQService_FindByUIN(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -677,7 +663,7 @@ func TestICQService_FindByUIN(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByUIN(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByUIN(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -743,6 +729,7 @@ func TestICQService_FindByUIN2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -767,11 +754,6 @@ func TestICQService_FindByUIN2(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -820,7 +802,7 @@ func TestICQService_FindByUIN2(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByUIN2(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByUIN2(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -906,12 +888,14 @@ func TestICQService_FindByWhitePages(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
 											wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
-												Message: wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+												Message: wire.ICQ_0x07DA_0x01A4_DBQueryMetaReplyUserFound{
 													ICQMetadata: wire.ICQMetadata{
 														UIN:     11111111,
 														ReqType: wire.ICQDBQueryMetaReply,
@@ -943,6 +927,7 @@ func TestICQService_FindByWhitePages(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -967,11 +952,6 @@ func TestICQService_FindByWhitePages(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -1024,7 +1004,7 @@ func TestICQService_FindByWhitePages(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByICQInterests(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByICQInterests(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -1118,12 +1098,14 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
 											wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
-												Message: wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+												Message: wire.ICQ_0x07DA_0x01A4_DBQueryMetaReplyUserFound{
 													ICQMetadata: wire.ICQMetadata{
 														UIN:     11111111,
 														ReqType: wire.ICQDBQueryMetaReply,
@@ -1155,6 +1137,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1179,11 +1162,6 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -1277,6 +1255,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1301,11 +1280,6 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 														Gender:        2,
 														Age:           25,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -1467,6 +1441,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1491,11 +1466,6 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 														Gender:        2,
 														Age:           25,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -1552,6 +1522,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1606,6 +1577,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1670,6 +1642,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1776,6 +1749,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -1800,11 +1774,6 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 														Gender:        1,
 														Age:           21,
 													},
-													LastMessageFooter: &struct {
-														FoundUsersLeft uint32
-													}{
-														FoundUsersLeft: 0,
-													},
 												},
 											}),
 										},
@@ -1863,7 +1832,7 @@ func TestICQService_FindByWhitePages2(t *testing.T) {
 				timeNow:          tt.timeNow,
 				userFinder:       userFinder,
 			}
-			err := s.FindByWhitePages2(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FindByWhitePages2(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -1980,6 +1949,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2024,6 +1995,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2063,6 +2036,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2089,6 +2064,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2115,6 +2092,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2155,6 +2134,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2184,6 +2165,8 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
+									Flags:     wire.SNACFlagsMoreToCome,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2231,6 +2214,7 @@ func TestICQService_FullUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2319,7 +2303,7 @@ func TestICQService_FullUserInfo(t *testing.T) {
 				timeNow:        tt.timeNow,
 				userFinder:     userFinder,
 			}
-			err := s.FullUserInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.FullUserInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -2396,6 +2380,7 @@ func TestICQService_OfflineMsgReq(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2428,6 +2413,7 @@ func TestICQService_OfflineMsgReq(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2460,6 +2446,7 @@ func TestICQService_OfflineMsgReq(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2529,6 +2516,7 @@ func TestICQService_OfflineMsgReq(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2575,7 +2563,7 @@ func TestICQService_OfflineMsgReq(t *testing.T) {
 				feedbagSenderCalls++
 				return nil
 			}
-			err := s.OfflineMsgReq(context.Background(), tt.instance, tt.seq)
+			err := s.OfflineMsgReq(context.Background(), wire.SNACFrame{RequestID: 1234}, tt.instance, tt.seq)
 			assert.NoError(t, err)
 			assert.Equal(t, tt.wantICBMSenderCalls, feedbagSenderCalls)
 		})
@@ -2661,6 +2649,7 @@ func TestICQService_SetAffiliations(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2730,7 +2719,7 @@ func TestICQService_SetAffiliations(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetAffiliations(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetAffiliations(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.ErrorIs(t, err, tt.wantErr)
 		})
 	}
@@ -2769,6 +2758,7 @@ func TestICQService_SetEmails(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -2805,7 +2795,7 @@ func TestICQService_SetEmails(t *testing.T) {
 				logger:         slog.Default(),
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetEmails(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetEmails(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -2999,6 +2989,7 @@ func TestICQService_SetICQInfo(t *testing.T) {
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.ICQ,
 			SubGroup:  wire.ICQDBReply,
+			RequestID: 1234,
 		},
 		Body: wire.SNAC_0x15_0x02_DBReply{
 			TLVRestBlock: wire.TLVRestBlock{
@@ -3285,7 +3276,7 @@ func TestICQService_SetICQInfo(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetICQInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetICQInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			if tt.wantErr != nil {
 				assert.ErrorIs(t, err, tt.wantErr)
 			} else {
@@ -3318,6 +3309,7 @@ func TestICQService_SetICQPhone(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3354,7 +3346,7 @@ func TestICQService_SetICQPhone(t *testing.T) {
 				logger:         slog.Default(),
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetICQPhone(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetICQPhone(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -3421,6 +3413,7 @@ func TestICQService_SetBasicInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3464,7 +3457,7 @@ func TestICQService_SetBasicInfo(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetBasicInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetBasicInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -3532,6 +3525,7 @@ func TestICQService_SetInterests(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3596,7 +3590,7 @@ func TestICQService_SetInterests(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetInterests(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetInterests(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.ErrorIs(t, err, tt.wantErr)
 		})
 	}
@@ -3652,6 +3646,7 @@ func TestICQService_SetMoreInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3695,7 +3690,7 @@ func TestICQService_SetMoreInfo(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetMoreInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetMoreInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -3738,6 +3733,7 @@ func TestICQService_SetPermissions(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3790,6 +3786,7 @@ func TestICQService_SetPermissions(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3833,7 +3830,7 @@ func TestICQService_SetPermissions(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetPermissions(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetPermissions(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -3874,6 +3871,7 @@ func TestICQService_SetUserNotes(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -3917,7 +3915,7 @@ func TestICQService_SetUserNotes(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetUserNotes(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetUserNotes(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -3980,6 +3978,7 @@ func TestICQService_SetWorkInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -4023,7 +4022,7 @@ func TestICQService_SetWorkInfo(t *testing.T) {
 				userUpdater:    userUpdater,
 				messageRelayer: messageRelayer,
 			}
-			err := s.SetWorkInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.SetWorkInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -4082,6 +4081,7 @@ func TestICQService_ShortUserInfo(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -4140,7 +4140,7 @@ func TestICQService_ShortUserInfo(t *testing.T) {
 				timeNow:        tt.timeNow,
 				userFinder:     userFinder,
 			}
-			err := s.ShortUserInfo(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.ShortUserInfo(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}
@@ -4175,6 +4175,7 @@ func TestICQService_XMLReqData(t *testing.T) {
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.ICQ,
 									SubGroup:  wire.ICQDBReply,
+									RequestID: 1234,
 								},
 								Body: wire.SNAC_0x15_0x02_DBReply{
 									TLVRestBlock: wire.TLVRestBlock{
@@ -4210,7 +4211,7 @@ func TestICQService_XMLReqData(t *testing.T) {
 				messageRelayer: messageRelayer,
 				timeNow:        tt.timeNow,
 			}
-			err := s.XMLReqData(context.Background(), tt.instance, tt.req, tt.seq)
+			err := s.XMLReqData(context.Background(), tt.instance, wire.SNACFrame{RequestID: 1234}, tt.req, tt.seq)
 			assert.NoError(t, err)
 		})
 	}

+ 11 - 11
scripts/populate_icq_users.sh

@@ -144,7 +144,7 @@ set_profile 123401 '{
     "fax": "+44-555-794-6095-ext-1234567",
     "address": "221 Baker Lane, Marylebone, Westminster, Central London District",
     "cell_phone": "+44-555-770-0901-ext-1234567",
-    "zip": "NW1 6XE ABCD",
+    "zip": "10001",
     "country_code": 44,
     "gmt_offset": 0,
     "publish_email": true
@@ -167,7 +167,7 @@ set_profile 123401 '{
     "address": "Broadcasting Centre Portland Place Westminster London England Un",
     "city": "London Borough of Westminster and Greater Metropolitan Area Dist",
     "state": "Greater London Metropolitan County Area with Extended Regional B",
-    "zip": "W1A 1AA BCDE",
+    "zip": "10002",
     "country_code": 44,
     "phone": "+44-555-758-0446-ext-12345",
     "fax": "+44-555-758-0446-ext-12345",
@@ -276,7 +276,7 @@ set_profile 123403 '{
     "fax": "+380-555-044-0101",
     "address": "Khreshchatyk 12, apt 45",
     "cell_phone": "+380-555-067-0100",
-    "zip": "01001",
+    "zip": "1001",
     "country_code": 380,
     "gmt_offset": 2,
     "publish_email": true
@@ -299,7 +299,7 @@ set_profile 123403 '{
     "address": "Velyka Vasylkivska 30",
     "city": "Kyiv",
     "state": "Kyiv",
-    "zip": "01004",
+    "zip": "1004",
     "country_code": 380,
     "phone": "+380-555-044-7000",
     "fax": "",
@@ -343,7 +343,7 @@ set_profile 123404 '{
     "fax": "",
     "address": "Herzl Blvd 42",
     "cell_phone": "+972-555-054-0100",
-    "zip": "6688212",
+    "zip": "66882",
     "country_code": 972,
     "gmt_offset": 2,
     "publish_email": false
@@ -366,7 +366,7 @@ set_profile 123404 '{
     "address": "Innovation Park 30",
     "city": "Tel Aviv",
     "state": "Tel Aviv",
-    "zip": "6971009",
+    "zip": "69710",
     "country_code": 972,
     "phone": "+972-555-030-0200",
     "fax": "+972-555-030-0201",
@@ -384,7 +384,7 @@ set_profile 123404 '{
     "past_code2": 304, "past_keyword2": "Military Service",
     "past_code3": 305, "past_keyword3": "Firewall Systems Ltd",
     "current_code1": 210, "current_keyword1": "National Cyber Directorate",
-    "current_code2": 316, "current_keyword2": "Tel Aviv Tech Hub",
+    "current_code2": 216, "current_keyword2": "Tel Aviv Tech Hub",
     "current_code3": 0, "current_keyword3": ""
   },
   "permissions": {
@@ -409,7 +409,7 @@ set_profile 123405 '{
     "fax": "+81-555-200-0101",
     "address": "Shibuya 2-21-1",
     "cell_phone": "+81-555-900-0100",
-    "zip": "150-0002",
+    "zip": "15000",
     "country_code": 81,
     "gmt_offset": 9,
     "publish_email": true
@@ -432,7 +432,7 @@ set_profile 123405 '{
     "address": "Kamitoba 11-1",
     "city": "Kyoto",
     "state": "Kyoto",
-    "zip": "601-8501",
+    "zip": "60185",
     "country_code": 81,
     "phone": "+81-555-750-0200",
     "fax": "",
@@ -475,7 +475,7 @@ set_profile 123406 '{
     "fax": "",
     "address": "Av Ipiranga 1578",
     "cell_phone": "+55-555-119-0100",
-    "zip": "01210-200",
+    "zip": "12102",
     "country_code": 55,
     "gmt_offset": 253,
     "publish_email": true
@@ -498,7 +498,7 @@ set_profile 123406 '{
     "address": "",
     "city": "Sao Paulo",
     "state": "SP",
-    "zip": "",
+    "zip": "12102",
     "country_code": 55,
     "phone": "",
     "fax": "",

+ 21 - 21
server/oscar/handler.go

@@ -523,7 +523,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 
 	switch icqMD.ReqType {
 	case wire.ICQDBQueryOfflineMsgReq:
-		return rt.OfflineMsgReq(ctx, instance, icqMD.Seq)
+		return rt.OfflineMsgReq(ctx, inFrame, instance, icqMD.Seq)
 	case wire.ICQDBQueryDeleteMsgReq:
 		return rt.DeleteMsgReq(ctx, instance, icqMD.Seq)
 	case wire.ICQDBQueryMetaReq:
@@ -541,19 +541,19 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := binary.Read(buf, binary.LittleEndian, &userInfo); err != nil {
 				return nil
 			}
-			return rt.ShortUserInfo(ctx, instance, userInfo, icqMD.Seq)
+			return rt.ShortUserInfo(ctx, instance, inFrame, userInfo, icqMD.Seq)
 		case wire.ICQDBQueryMetaReqFullInfo, wire.ICQDBQueryMetaReqFullInfo2:
 			userInfo := wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN{}
 			if err := binary.Read(buf, binary.LittleEndian, &userInfo); err != nil {
 				return nil
 			}
-			return rt.FullUserInfo(ctx, instance, userInfo, icqMD.Seq)
+			return rt.FullUserInfo(ctx, instance, inFrame, userInfo, icqMD.Seq)
 		case wire.ICQDBQueryMetaReqXMLReq:
 			req := wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq{}
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.XMLReqData(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.XMLReqData(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetPermissions:
@@ -561,7 +561,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetPermissions(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetPermissions(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetICQPhone:
@@ -569,7 +569,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetICQPhone(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetICQPhone(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchByUIN:
@@ -577,7 +577,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByUIN(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByUIN(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchByUIN2:
@@ -585,7 +585,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByUIN2(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByUIN2(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchByEmail:
@@ -593,7 +593,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByICQEmail(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByICQEmail(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchByEmail3:
@@ -601,7 +601,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByEmail3(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByEmail3(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchByDetails:
@@ -609,7 +609,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByICQName(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByICQName(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchWhitePages:
@@ -617,7 +617,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByICQInterests(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByICQInterests(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSearchWhitePages2:
@@ -625,7 +625,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.FindByWhitePages2(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.FindByWhitePages2(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetBasicInfo:
@@ -633,7 +633,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetBasicInfo(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetBasicInfo(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetWorkInfo:
@@ -641,7 +641,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetWorkInfo(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetWorkInfo(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetMoreInfo:
@@ -649,7 +649,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetMoreInfo(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetMoreInfo(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetNotes:
@@ -657,7 +657,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetUserNotes(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetUserNotes(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetEmails:
@@ -665,7 +665,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetEmails(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetEmails(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetInterests:
@@ -673,7 +673,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetInterests(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetInterests(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetAffiliations:
@@ -681,7 +681,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetAffiliations(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetAffiliations(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqSetFullInfo:
@@ -689,7 +689,7 @@ func (rt Handler) ICQDBQuery(ctx context.Context, instance *state.SessionInstanc
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
 				return err
 			}
-			if err := rt.SetICQInfo(ctx, instance, req, icqMD.Seq); err != nil {
+			if err := rt.SetICQInfo(ctx, instance, inFrame, req, icqMD.Seq); err != nil {
 				return err
 			}
 		case wire.ICQDBQueryMetaReqStat0a8c,

+ 34 - 26
server/oscar/handler_test.go

@@ -2816,6 +2816,10 @@ func TestHandler_ICBMParameterQuery(t *testing.T) {
 }
 
 func TestHandler_ICQDBQuery(t *testing.T) {
+	frame := wire.SNACFrame{
+		FoodGroup: wire.ICQ,
+		SubGroup:  wire.ICQDBQuery,
+	}
 	type ICQMetaRequest struct {
 		wire.ICQMetadata
 		ReqSubType  uint16
@@ -2823,11 +2827,13 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 	}
 	type reqParams struct {
 		instance *state.SessionInstance
+		inFrame  wire.SNACFrame
 		inBody   wire.SNAC_0x15_0x02_BQuery
 		seq      uint16
 		wantErr  error
 	}
 	type mockParam struct {
+		frame   any
 		req     any
 		wantErr error
 	}
@@ -3084,6 +3090,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			name: "MetaReqSearchByUIN2 - happy path",
 			reqParams: reqParams{
 				instance: state.NewSession().AddInstance(),
+				inFrame:  frame,
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
@@ -3110,6 +3117,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			},
 			allMockParams: allMockParams{
 				findByUIN2: &mockParam{
+					frame: frame,
 					req: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{
 						TLVRestBlock: wire.TLVRestBlock{
 							TLVList: wire.TLVList{
@@ -3156,6 +3164,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			name: "MetaReqSearchByEmail3 - happy path",
 			reqParams: reqParams{
 				instance: state.NewSession().AddInstance(),
+				inFrame:  frame,
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
@@ -3182,6 +3191,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			},
 			allMockParams: allMockParams{
 				findByEmail3: &mockParam{
+					frame: frame,
 					req: wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{
 						TLVRestBlock: wire.TLVRestBlock{
 							TLVList: wire.TLVList{
@@ -3260,6 +3270,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			name: "MetaReqSearchWhitePages2 - happy path",
 			reqParams: reqParams{
 				instance: state.NewSession().AddInstance(),
+				inFrame:  frame,
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
@@ -3286,6 +3297,7 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			},
 			allMockParams: allMockParams{
 				findByWhitePages2: &mockParam{
+					frame: frame,
 					req: wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{
 						TLVRestBlock: wire.TLVRestBlock{
 							TLVList: wire.TLVList{
@@ -3749,87 +3761,87 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			switch {
 			case tt.allMockParams.fullUserInfo != nil:
 				icqService.EXPECT().
-					FullUserInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.fullUserInfo.req, tt.reqParams.seq).
+					FullUserInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.fullUserInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.fullUserInfo.wantErr)
 			case tt.allMockParams.shortUserInfo != nil:
 				icqService.EXPECT().
-					ShortUserInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.shortUserInfo.req, tt.reqParams.seq).
+					ShortUserInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.shortUserInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.shortUserInfo.wantErr)
 			case tt.allMockParams.xmlReqData != nil:
 				icqService.EXPECT().
-					XMLReqData(mock.Anything, tt.reqParams.instance, tt.allMockParams.xmlReqData.req, tt.reqParams.seq).
+					XMLReqData(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.xmlReqData.req, tt.reqParams.seq).
 					Return(tt.allMockParams.xmlReqData.wantErr)
 			case tt.allMockParams.setPermissions != nil:
 				icqService.EXPECT().
-					SetPermissions(mock.Anything, tt.reqParams.instance, tt.allMockParams.setPermissions.req, tt.reqParams.seq).
+					SetPermissions(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setPermissions.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setPermissions.wantErr)
 			case tt.allMockParams.setICQPhone != nil:
 				icqService.EXPECT().
-					SetICQPhone(mock.Anything, tt.reqParams.instance, tt.allMockParams.setICQPhone.req, tt.reqParams.seq).
+					SetICQPhone(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setICQPhone.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setICQPhone.wantErr)
 			case tt.allMockParams.findByUIN != nil:
 				icqService.EXPECT().
-					FindByUIN(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByUIN.req, tt.reqParams.seq).
+					FindByUIN(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByUIN.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByUIN.wantErr)
 			case tt.allMockParams.findByUIN2 != nil:
 				icqService.EXPECT().
-					FindByUIN2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByUIN2.req, tt.reqParams.seq).
+					FindByUIN2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByUIN2.frame, tt.allMockParams.findByUIN2.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByUIN2.wantErr)
 			case tt.allMockParams.findByEmail != nil:
 				icqService.EXPECT().
-					FindByICQEmail(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByEmail.req, tt.reqParams.seq).
+					FindByICQEmail(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByEmail.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByEmail.wantErr)
 			case tt.allMockParams.findByEmail3 != nil:
 				icqService.EXPECT().
-					FindByEmail3(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByEmail3.req, tt.reqParams.seq).
+					FindByEmail3(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByEmail3.frame, tt.allMockParams.findByEmail3.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByEmail3.wantErr)
 			case tt.allMockParams.findByDetails != nil:
 				icqService.EXPECT().
-					FindByICQName(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByDetails.req, tt.reqParams.seq).
+					FindByICQName(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByDetails.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByDetails.wantErr)
 			case tt.allMockParams.findByInterests != nil:
 				icqService.EXPECT().
-					FindByICQInterests(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByInterests.req, tt.reqParams.seq).
+					FindByICQInterests(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.findByInterests.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByInterests.wantErr)
 			case tt.allMockParams.findByWhitePages2 != nil:
 				icqService.EXPECT().
-					FindByWhitePages2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByWhitePages2.req, tt.reqParams.seq).
+					FindByWhitePages2(mock.Anything, tt.reqParams.instance, tt.allMockParams.findByWhitePages2.frame, tt.allMockParams.findByWhitePages2.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByWhitePages2.wantErr)
 			case tt.allMockParams.setBasicInfo != nil:
 				icqService.EXPECT().
-					SetBasicInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.setBasicInfo.req, tt.reqParams.seq).
+					SetBasicInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setBasicInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setBasicInfo.wantErr)
 			case tt.allMockParams.setWorkInfo != nil:
 				icqService.EXPECT().
-					SetWorkInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.setWorkInfo.req, tt.reqParams.seq).
+					SetWorkInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setWorkInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setWorkInfo.wantErr)
 			case tt.allMockParams.setMoreInfo != nil:
 				icqService.EXPECT().
-					SetMoreInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.setMoreInfo.req, tt.reqParams.seq).
+					SetMoreInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setMoreInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setMoreInfo.wantErr)
 			case tt.allMockParams.setUserNotes != nil:
 				icqService.EXPECT().
-					SetUserNotes(mock.Anything, tt.reqParams.instance, tt.allMockParams.setUserNotes.req, tt.reqParams.seq).
+					SetUserNotes(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setUserNotes.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setUserNotes.wantErr)
 			case tt.allMockParams.setEmails != nil:
 				icqService.EXPECT().
-					SetEmails(mock.Anything, tt.reqParams.instance, tt.allMockParams.setEmails.req, tt.reqParams.seq).
+					SetEmails(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setEmails.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setEmails.wantErr)
 			case tt.allMockParams.setInterests != nil:
 				icqService.EXPECT().
-					SetInterests(mock.Anything, tt.reqParams.instance, tt.allMockParams.setInterests.req, tt.reqParams.seq).
+					SetInterests(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setInterests.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setInterests.wantErr)
 			case tt.allMockParams.setAffiliations != nil:
 				icqService.EXPECT().
-					SetAffiliations(mock.Anything, tt.reqParams.instance, tt.allMockParams.setAffiliations.req, tt.reqParams.seq).
+					SetAffiliations(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setAffiliations.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setAffiliations.wantErr)
 			case tt.allMockParams.setFullInfo != nil:
 				icqService.EXPECT().
-					SetICQInfo(mock.Anything, tt.reqParams.instance, tt.allMockParams.setFullInfo.req, tt.reqParams.seq).
+					SetICQInfo(mock.Anything, tt.reqParams.instance, frame, tt.allMockParams.setFullInfo.req, tt.reqParams.seq).
 					Return(tt.allMockParams.setFullInfo.wantErr)
 			case tt.allMockParams.offlineMsgReq != nil:
 				icqService.EXPECT().
-					OfflineMsgReq(mock.Anything, tt.reqParams.instance, tt.reqParams.seq).
+					OfflineMsgReq(mock.Anything, frame, tt.reqParams.instance, tt.reqParams.seq).
 					Return(tt.allMockParams.offlineMsgReq.wantErr)
 			case tt.allMockParams.deleteMsgReq != nil:
 				icqService.EXPECT().
@@ -3847,10 +3859,6 @@ func TestHandler_ICQDBQuery(t *testing.T) {
 			buf := &bytes.Buffer{}
 			assert.NoError(t, wire.MarshalBE(tt.reqParams.inBody, buf))
 
-			frame := wire.SNACFrame{
-				FoodGroup: wire.ICQ,
-				SubGroup:  wire.ICQDBQuery,
-			}
 			err := h.Handle(context.TODO(), wire.BOS, tt.reqParams.instance, frame, buf, nil, config.Listener{})
 			assert.ErrorIs(t, err, tt.reqParams.wantErr)
 		})
@@ -3877,7 +3885,7 @@ func TestHandler_ICQDBQuery_QIP2005UINSearchBug(t *testing.T) {
 
 	instance := state.NewSession().AddInstance()
 	icqService.EXPECT().
-		FindByUIN2(mock.Anything, instance, expect, uint16(1)).
+		FindByUIN2(mock.Anything, instance, wire.SNACFrame{}, expect, uint16(1)).
 		Return(nil)
 
 	h := Handler{

Dosya farkı çok büyük olduğundan ihmal edildi
+ 242 - 163
server/oscar/mock_icq_service_test.go


+ 21 - 21
server/oscar/types.go

@@ -116,27 +116,27 @@ type ICBMService interface {
 
 type ICQService interface {
 	DeleteMsgReq(ctx context.Context, instance *state.SessionInstance, seq uint16) error
-	FindByICQName(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error
-	FindByICQEmail(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error
-	FindByEmail3(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error
-	FindByICQInterests(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error
-	FindByUIN(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
-	FindByUIN2(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error
-	FindByWhitePages2(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error
-	FullUserInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
-	OfflineMsgReq(ctx context.Context, instance *state.SessionInstance, seq uint16) error
-	SetAffiliations(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error
-	SetBasicInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo, seq uint16) error
-	SetEmails(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails, seq uint16) error
-	SetICQInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo, seq uint16) error
-	SetInterests(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests, seq uint16) error
-	SetMoreInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo, seq uint16) error
-	SetPermissions(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions, seq uint16) error
-	SetICQPhone(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone, seq uint16) error
-	SetUserNotes(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes, seq uint16) error
-	SetWorkInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo, seq uint16) error
-	ShortUserInfo(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo, seq uint16) error
-	XMLReqData(ctx context.Context, instance *state.SessionInstance, inBody wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq, seq uint16) error
+	FindByICQName(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error
+	FindByICQEmail(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error
+	FindByEmail3(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error
+	FindByICQInterests(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error
+	FindByUIN(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
+	FindByUIN2(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error
+	FindByWhitePages2(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error
+	FullUserInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
+	OfflineMsgReq(ctx context.Context, inFrame wire.SNACFrame, instance *state.SessionInstance, seq uint16) error
+	SetAffiliations(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error
+	SetBasicInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo, seq uint16) error
+	SetEmails(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x040B_DBQueryMetaReqSetEmails, seq uint16) error
+	SetICQInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo, seq uint16) error
+	SetInterests(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0410_DBQueryMetaReqSetInterests, seq uint16) error
+	SetMoreInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo, seq uint16) error
+	SetPermissions(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0424_DBQueryMetaReqSetPermissions, seq uint16) error
+	SetICQPhone(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0654_DBQueryMetaReqSetICQPhone, seq uint16) error
+	SetUserNotes(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0406_DBQueryMetaReqSetNotes, seq uint16) error
+	SetWorkInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x03F3_DBQueryMetaReqSetWorkInfo, seq uint16) error
+	ShortUserInfo(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x04BA_DBQueryMetaReqShortInfo, seq uint16) error
+	XMLReqData(ctx context.Context, instance *state.SessionInstance, inFrame wire.SNACFrame, inBody wire.ICQ_0x07D0_0x0898_DBQueryMetaReqXMLReq, seq uint16) error
 }
 
 type LocateService interface {

+ 4 - 0
wire/frames.go

@@ -50,6 +50,10 @@ type SNACFrame struct {
 	RequestID uint32
 }
 
+// SNACFlagsMoreToCome is set on SNAC frames that are part of a multi-packet
+// response (bit 0). Use it on every fragment except the last.
+const SNACFlagsMoreToCome uint16 = 0x0001
+
 // ReqIDFromServer is the SNAC frame Request ID value that indicates the SNAC
 // is initiated by the server. Some clients, such as the Java AIM 1.1.19,
 // completely fail to process some server SNACs if the high bit is not set on

+ 8 - 13
wire/snacs.go

@@ -2339,25 +2339,20 @@ type ICQ_0x07DA_0x00C8_DBQueryMetaReplyBasicInfo struct {
 	PublishEmail uint8
 }
 
-type ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound struct {
+type ICQ_0x07DA_0x01A4_DBQueryMetaReplyUserFound struct {
 	ICQMetadata
 	ReqSubType uint16
 	Success    uint8
 	Details    ICQUserSearchRecord `oscar:"len_prefix=uint16"`
-	// LastMessageFooter is set only on the last message in the batch
-	LastMessageFooter *struct {
-		FoundUsersLeft uint32
-	} `oscar:"optional"`
+	Buffer     uint16
 }
 
-// LastResult flags the message as the last message in the search results.
-func (s *ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound) LastResult() {
-	s.ReqSubType = ICQDBQueryMetaReplyLastUserFound
-	s.LastMessageFooter = &struct {
-		FoundUsersLeft uint32
-	}{
-		FoundUsersLeft: 0,
-	}
+type ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound struct {
+	ICQMetadata
+	ReqSubType uint16
+	Success    uint8
+	Details    ICQUserSearchRecord `oscar:"len_prefix=uint16"`
+	UsersLeft  uint32
 }
 
 type ICQ_0x07DA_0x0104_DBQueryMetaReplyShortInfo struct {

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor