Skip to content

WIP: ICQ legacy protocol support (V2-V5) based on iserverd#171

Merged
mk6i merged 2 commits intomk6i:mainfrom
VasylSamoilov:main
Apr 5, 2026
Merged

WIP: ICQ legacy protocol support (V2-V5) based on iserverd#171
mk6i merged 2 commits intomk6i:mainfrom
VasylSamoilov:main

Conversation

@VasylSamoilov
Copy link
Copy Markdown
Contributor

This PR introduces initial support for legacy, UDP-based ICQ protocols used by pre-OSCAR clients. The implementation focuses on protocol compatibility across multiple historical versions and is still a work in progress. Feedback and criticism are very welcome.

Supported protocol versions

V2: Partial login support
Tested with Windows 3.11 16-bit client

V3: Functional login and messaging
Tested with ICQ Groupware

V4: Initial login succeeds
ICQ 98a specific version only

V5: Fully working
Tested with ICQ99

Known limitations

Later ICQ 98a releases use V5 with TCP_VER=4 (V4 encapsulated in V5 framing).
This mode is not yet supported.

Implementation notes

Based on the iserverd reference implementation.

Includes protocol documentation and research notes on client internals to aid future development and maintenance.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 7, 2026

Hello, I am very excited about this PR.

Unfortunately, this is problematic:

Reverse engineering notes from analyzing ICQ.EXE (1996 Mirabilis ICQ client) using Ghidra.

If I knowingly merge code that is based on decompilation, that puts my project at risk of a copyright takedown.

I can only accept changes that are made based on clean reverse engineering techniques (pcap analysis, etc).

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Please don't merge, this is WIP and will change as I will test it more. It's just to show conceptually that this is possible. Feel free to review and suggest improvements. Disassembly didn't produce any substantial assistance, v4 is based on documentation from internet archive, v2 is from iserverd and trial and error, both don't actually are in usable state, and more for reference and future development, if somebody will come up with dumps or ideas, as protocol flow is still unclear.

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Cleaned up work notes and documentation.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 7, 2026

Thanks for letting me know. I’ll review the code and provide some early feedback.

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

It turned out that old versions of open-source clients are good protocol
references. With the help of the Internet Archive, center (centericq, later
centerim), licq, and additional testing, basic functionality now works for
protocol versions v3, v4, and v5, including login, online status,
authorization, messaging, and search.

Protocol v2 should also work, but it is unclear which ICQ version
corresponds to v2. v2 was widely used by open-source clients for a long
time because the protocol is very simple.

The implementation is now ready for proper review, although many
architectural decisions are likely sub-optimal.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 8, 2026

Excellent, I’ll review over the next couple days

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

I found ICQ version with protocol 1 and 2. These are ICQ 1.02 beta from 1996 and ICQ 1.111 Beta from 1997 respectively, but they are strictly checking for windows 95 or nt 4.0 (don't want to install in windows 98 even). 1997 ICQ for windows 3.11 1.111 beta seems also to be a variant of protocol 2. Either way, I can't figure out their FirstLogin flow (first login after installation), it's the same problem I solved with protocol V4. Open source clients only do normal login (that is different state machine). Either way, these clients are very niche, and how much I would like to support them (especially windows 3.11 one), so far I was not able to figure it out.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 20, 2026

How are you configuring the server hostname in the client? I tried overriding the hostname in HKEY_CURRENT_USER\Software\Mirabilis\ICQ\DefaultPrefs\Default Server Host and the client doesn't appear to connect.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 20, 2026

Nevermind, figured it out. Set the hostname by clicking "For admin use" on sign-on.

Comment thread server/http/mgmt_api.go Outdated
@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Feb 20, 2026

I'm able to connect and chat between ICQ99 and ICQ2000b—pretty cool! I will focus on reviewing the code architecture next.

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

VasylSamoilov commented Feb 20, 2026

I'm able to connect and chat between ICQ99 and ICQ2000b—pretty cool! I will focus on reviewing the code architecture next.

One small message for a client, one giant leap for interoperability. :)

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Just checking in on this PR. Is this something you’d like to move forward with? No rush. Happy to keep iterating on it if needed, or close it out if it’s not a direction you want to take.

Thanks.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Mar 26, 2026

Hey @VasylSamoilov I still would like to move forward with this PR. 👍

My main architectural concern with this PR is that there is net new logic added to the foodgroup package, which is the home of core OSCAR behavior; mixing in legacy-protocol concerns blurs boundaries.

It appears that ICQLegacyService duplicates code that already exists in the foodgroup package. For example, ICQLegacyService.GetOfflineMessages is already provided by ICQService.OfflineMsgReq.

func (s ICQService) OfflineMsgReq(ctx context.Context, instance *state.SessionInstance, seq uint16) error {

I think much of the legacy ICQ server can be implemented by passing messages to and from the existing ICQService in the foodgroup package.

I invite you to you look at the toc package to see how a server can integrate with OSCAR in a decoupled way, without modifying the foodgroup package.

Here are some guidelines:

  • Keep foodgroup unchanged. (Some modifications may be acceptable, depending on the context.)
  • Plug ICQLegacyService into OSCAR by consuming the foodgroup services via interfaces. See the toc package for an example.
  • The ICQLegacyService is responsible for translating legacy messages to OSCAR, and vice-versa.

Let me know if this makes sense or not :)

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Hey @mk6i, I went ahead and followed your guideline for the rearchitecture.

  • Relocated ICQLegacyService, all the request/response types, and the session interfaces from foodgroup/ into server/icq_legacy/, following the same pattern as server/toc/. The foodgroup package no longer has any knowledge of legacy ICQ.
  • Defined narrow interfaces in types.go (same approach as TOC).
  • Moved the service, types, and session interfaces into the icq_legacy package.
  • Removed the legacySessionManagerAdapter from factory.go. No longer needed since the service and session manager live in the same package.
  • Updated all version handlers and packet builders to use local types instead of importing foodgroup.
  • Merged with latest upstream and resolved the interface changes (AddSession variadic opts, BroadcastBuddyDeparted signature, RemoveSession signature)

Good news that it still works! Please have a look.

Copy link
Copy Markdown
Owner

@mk6i mk6i left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of good progress here!

  • Let's find a way to remove that last ICQ legacy dependency in ICBM.
  • Also don't forget to run go fmt ./...
  • Feel free to join our Discord server :)

Comment thread foodgroup/icbm.go Outdated
Comment thread wire/snacs.go
@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

@mk6i You are correct. Cleaned up a little bit.

  • Removed LegacyMessageSender from icbm.go. Legacy ICQ sessions register with the unified OSCAR session manager, so RelayToScreenName delivers messages through the bridge's message pump.
  • Ran gofmt -s across the codebase.
  • Removed admin UI
  • UDP listener forced to IPv4 (just in case)

Copy link
Copy Markdown
Owner

@mk6i mk6i left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for those changes. Now that the ICQ legacy logic is isolated to 1 package, I’ll focus my review on the internals of the package itself.

Will likely continue the review tonight (my time).

Comment thread server/icq_legacy/wire_legacy.go
Comment thread server/icq_legacy/wire_legacy_commands.go
Comment thread server/icq_legacy/wire_legacy_crypt.go
Comment thread cmd/server/factory.go
Comment thread config/ssl/settings.env
Comment thread foodgroup/types.go Outdated
Comment thread foodgroup/types.go Outdated
@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Default supported versions are now V3, V4, V5. These are production-ready, covers nearly all known icq clients including groupware. V1 is logging only (not implemented). V2 is disabled by default but the login sequence is nearly complete. With some help on the remaining handshake steps we could enable it going forward - and get icq for windows 3.11 up and running. I can provide dosbox image with windows 3.11, icq and aim clients pre-installed in someone is willing to help, would appreciate it.

SetHomepageCategory removed from foodgroup ICQUserUpdater - only used by icq_legacy. DeleteUser stays on foodgroup UserManager because the management HTTP API (mgmt_api.go) uses it for account deletion.

Other requested and not requested :) changes:

  • moved wire/icq_legacy*.go into server/icq_legacy/
  • added per-version ICQ_LEGACY_DIRECT_CONNECTIONS config
  • renamed TCPVersion -> DCVersion (naming was confusing, TCP in icq protocol is parameter for client-to-client connection protocol)
  • updated config generator to handle nested structs.

Copy link
Copy Markdown
Owner

@mk6i mk6i left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please squash your commits and I'll be ready to merge!

In a follow-up PR, would you please write user documentation that explains how to set up clients for each protocol?

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

I did it. V2 login works! Now a little bit of testing is required.

image

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

And I broke legacy-oscar bridge during refactoring. Now I'm scratching my head how to fix it properly and don't break the architecture. Stay tuned :)

Comment thread foodgroup/buddy.go Outdated
@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

@mk6i I'm really pushing the envelope of my capabilities here. Attempted a minimal-impact bridge, but this area is more complex than expected.
At least one knows defect: “Requires authorization” state is not currently propagated from ICQ to OSCAR. OSCAR clients treat ICQ users as not requiring authorization.
Maybe some other bugs here and there. Please review and advice. Will appreciate that.

Comment thread foodgroup/buddy.go Outdated
@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 3, 2026

@mk6i I'm really pushing the envelope of my capabilities here. Attempted a minimal-impact bridge, but this area is more complex than expected. At least one knows defect: “Requires authorization” state is not currently propagated from ICQ to OSCAR. OSCAR clients treat ICQ users as not requiring authorization. Maybe some other bugs here and there. Please review and advice. Will appreciate that.

How do I reproduce? Login with incorrect credentials?

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

@mk6i I'm really pushing the envelope of my capabilities here. Attempted a minimal-impact bridge, but this area is more complex than expected. At least one knows defect: “Requires authorization” state is not currently propagated from ICQ to OSCAR. OSCAR clients treat ICQ users as not requiring authorization. Maybe some other bugs here and there. Please review and advice. Will appreciate that.

How do I reproduce? Login with incorrect credentials?

No. When a user adds someone to their contact list, that entry should be placed in a “waiting authorization” state. The other party cannot add them to their own contact list unless approval is granted on the remote side. This is handled via a dedicated authorization request/response message flow.

This mechanism already functions correctly on the ICQ-ICQ pair and ICQ side of ICQ-OSCAR pair (OSCAR contact requires explicit authorization for visibility). However, for OSCAR, ICQ users are currently added as visible/authorized by default.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 3, 2026

Which OSCAR client are you using to test?

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Which OSCAR client are you using to test?

ICQ 2003b build # 3916

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 3, 2026

Does authorization work if you set AuthRequired=true on the users sql table?

We set this to false by default recently, I don’t think it was intentional.

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

I'll check tomorrow and report. That may be the root cause.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 4, 2026

Please pull main, I restored requiring authorization by default

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Please pull main, I restored requiring authorization by default

It was not only that. But it helped. Ready for review.

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 4, 2026

Excellent. Can you squash your commits?

Comment thread config/ssl/settings.env
export ICQ_LEGACY_UDP_LISTENER=0.0.0.0:4000

# Comma-separated list of supported ICQ protocol versions. Valid values: 1, 2,
# 3, 4, 5. V1 is logging only (not implemented). V2 is partial (login sequence
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t v2 fully working now?

Comment thread foodgroup/buddy.go Outdated
Comment thread foodgroup/feedbag.go

if err := s.feedbagManager.FeedbagUpsert(ctx, instance.IdentScreenName(), items); err != nil {
return nil, err
// Check which buddy items require authorization. Items that need auth
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing this

Comment thread foodgroup/feedbag.go
return nil
}

// RequestAuthorizeToHost forwards an authorization request from the user who
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, which ICQ versions send this?

UDP-based ICQ legacy protocol handlers for pre-OSCAR clients. Implements
login, messaging, contacts, search, user info, status notifications,
registration, and offline messages. Based on iserverd and old versions of
licq source code.

Cross-protocol bridge between legacy ICQ and OSCAR:
- Bidirectional presence notifications (status, online/offline)
- Message routing (text, URL, contacts, auth messages)
- Authorization enforcement (feedbag 0x000E, request/grant/deny flow)
- Profile save persistence for all protocol versions
- Status mapping between legacy ICQ and OSCAR status values

- V2: Based on licq source, untested
- V3: Tested with ICQ Groupware
- V4: Based on licq, tested with ICQ 98a
- V5: Tested with ICQ99
@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

Excellent. Can you squash your commits?

Squashed.

Copy link
Copy Markdown
Owner

@mk6i mk6i left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m ready to merge. Just curious about v2: you posted a screenshot of a successful v2 login sequence, but the docs say v2 is incomplete. What’s the status of v2?

UDP-based ICQ legacy protocol handlers for pre-OSCAR clients. Implements
login, messaging, contacts, search, user info, status notifications,
registration, and offline messages. Based on iserverd and old versions of
licq source code.

Cross-protocol bridge between legacy ICQ and OSCAR:
- Bidirectional presence notifications (status, online/offline)
- Message routing (text, URL, contacts, auth messages)
- Authorization enforcement (feedbag 0x000E, request/grant/deny flow)
- Profile save persistence for all protocol versions
- Status mapping between legacy ICQ and OSCAR status values
- V2: Based on licq source, tested with ICQ for windows 3.11
- V3: Tested with ICQ Groupware
- V4: Based on licq, tested with ICQ 98a
- V5: Tested with ICQ99
@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

I’m ready to merge. Just curious about v2: you posted a screenshot of a successful v2 login sequence, but the docs say v2 is incomplete. What’s the status of v2?

Ok, I have completed V2 protocol support and force-pushed squashed result. Please review.

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

So far it's the only ICQ server implementation that supports v2 and v4 protocol.

@mk6i mk6i merged commit d5ada5e into mk6i:main Apr 5, 2026
@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 5, 2026

👏

@mk6i
Copy link
Copy Markdown
Owner

mk6i commented Apr 5, 2026

@VasylSamoilov can you plz fix the go vet errors (see build failure)

@VasylSamoilov
Copy link
Copy Markdown
Contributor Author

yes, this and small test fix. #180

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants