gnuworld/README.md

329 lines
14 KiB
Markdown

# 🌐 GNUWorld — Servicii IRC pentru Undernet/P10
## Prezentare Generală
**GNUWorld** este un cadru (framework) de servicii IRC scris în **C++**, proiectat pentru rețele bazate pe protocolul **P10** (ircu/Undernet). Arhitectura este modulară, permițând încărcarea dinamică a clienților de servicii (CService, CControl, ChanFix, DroneScan etc.) prin intermediul bibliotecii `libtool` (`lt_dlopen`).
Proiectul este o adaptare și extensie a GNUWorld original, personalizat pentru nevoile rețelei **Underchat**.
---
## 📐 Arhitectura Generală
### Ierarhia de Clase Principale
```
NetworkTarget ← Clasa de bază (adresare numerică P10 base64: YY + XXX)
├── iClient ← Utilizator de rețea (nick, user, host, moduri, canale)
├── iServer ← Server de rețea (uplink, stare burst, flag-uri)
├── xClient (+ TimerHandler) ← Client de servicii (bază pentru module: cservice, ccontrol etc.)
└── xServer (+ ConnectionManager ← Serverul principal — inima sistemului
+ ConnectionHandler
+ NetworkTarget)
Channel ← Canal IRC (moduri, utilizatori, ban-uri)
ChannelUser ← Asociere utilizator-canal (op, voice, half-op)
xNetwork ← Container global — stochează toți clienții, serverele și canalele
```
### Componentele Cheie
| Componentă | Locație | Descriere |
|---|---|---|
| **xServer** | `include/server.h`, `src/server.cc`, `src/main.cc` | Serverul principal — bucla de evenimente, gestionarea conexiunii, rutarea mesajelor |
| **xClient** | `include/client.h` | Clasa de bază pentru toate modulele de servicii |
| **xNetwork** | `include/Network.h`, `src/Network.cc` | Containerul global al rețelei — tabele pentru clienți, servere, canale |
| **ConnectionManager** | `libgnuworld/ConnectionManager.h`, `libgnuworld/ConnectionManager.cc` | Gestionarea conexiunilor I/O prin `select()` |
| **iClient / iServer** | `include/iClient.h`, `include/iServer.h` | Reprezentarea entităților de rețea |
| **Channel / ChannelUser** | `include/Channel.h`, `include/ChannelUser.h` | Reprezentarea canalelor și a utilizatorilor din canale |
| **Baza de Date** | `db/gnuworldDB.h`, `db/pgsqlDB.h` | Strat abstract cu implementare PostgreSQL |
---
## 🔄 Bucla Principală de Evenimente
Punctul de intrare este `main()` din `src/main.cc`, care instanțiază `xServer` și apelează `run()``mainLoop()`.
### Fluxul `mainLoop()` (din `src/main.cc`, linia 335):
```
┌─────────────────────────────────────────────┐
│ while( keepRunning ) │
│ │
│ 1. Verifică reconectarea (dacă e cazul) │
│ 2. Verifică dacă este ultima iterație │
│ 3. Calculează timeout-ul pe baza timerelor │
│ 4. ConnectionManager::Poll(seconds) │
│ └─> select() pe toate socket-urile │
│ └─> handleRead() / handleWrite() │
│ └─> OnRead() → Process() → dispatch │
│ 5. CheckTimers() — execută timerele │
│ 6. Verifică semnalele (Signal::getSignal) │
│ 7. PostSignal() către module │
│ │
│ La ieșire: doShutdown() │
└─────────────────────────────────────────────┘
```
### Multiplexarea I/O — `select()`
`ConnectionManager::Poll()` (din `libgnuworld/ConnectionManager.cc`, linia 559) folosește apelul **`select()`** clasic:
- Iterează prin `handlerMap` (mapare `ConnectionHandler* → set<Connection*>`)
- Configurează `fd_set` (readfds / writefds) pentru fiecare conexiune
- Apelează `::select(1 + highestFD, &readfds, &writefds, 0, &to)`
- Procesează rezultatele: citire, scriere, conexiuni noi, timeout-uri
> ⚠️ **Observație:** Se folosește `select()` și nu `poll()`/`epoll()`. Aceasta este o limitare pentru rețele foarte mari (limita FD_SETSIZE, de obicei 1024).
---
## 🔗 Procesarea BURST și Sincronizarea cu Rețeaua
Sincronizarea cu rețeaua urmează protocolul P10 complet:
### Secvența de Burst:
```
1. msg_Server → Creează iServer pentru uplink, pornește burst-ul
2. msg_S → Adaugă servere noi (EVT_NETJOIN)
3. msg_N → Adaugă clienți de rețea (iClient) în timpul burst-ului
4. msg_B → BURST canale: moduri, utilizatori (base64), ban-uri
(libircu/msg_B.cc — 541 linii, parser complex)
5. msg_EB → End of Burst:
├─ setUseHoldBuffer(false) — eliberează bufferul reținut
├─ BurstClients() — trimite clienții de servicii
├─ BurstChannels() — trimite canalele serviciilor
├─ setBursting(false)
├─ PostEvent(EVT_BURST_CMPLT)
├─ Trimite EB + EA
└─ WriteBurstBuffer() — golește datele reținute
6. msg_EA → End of Burst Acknowledge — confirmă sincronizarea
```
### Mecanismul `burstHoldBuffer`:
În timpul burst-ului, `Write()` redirecționează datele într-un `burstHoldBuffer` separat (variabila `useHoldBuffer`). Aceasta previne trimiterea prematură de date către rețea înainte ca sincronizarea să fie completă. După `msg_EB`, datele sunt transferate în bufferul normal de ieșire prin `WriteBurstBuffer()`.
---
## 🧩 Sistemul de Module (Pluginuri)
### Încărcare Dinamică
Modulele sunt încărcate prin `libtool` (`lt_dlopen`) folosind clasa template `moduleLoader<>` din `include/moduleLoader.h`.
**Două tipuri de module:**
1. **Module de Servicii** (`xClient`) — încărcate prin `loadClients()`:
- Fiecare subclasează `xClient`
- Exportă o funcție fabrică: `_gnuwinit_<nume>(configFile)`
- Exemple: `mod.cservice`, `mod.ccontrol`, `mod.chanfix`
2. **Handlere de Comenzi Server** (`ServerCommandHandler`) — încărcate prin `loadCommandHandlers()`:
- Fiecare procesează un mesaj P10 specific (B, N, Q, M, J, etc.)
- Exportă: `_gnuwinit_<msg_X>(xServer*)`
- Locație: `libircu/msg_*.cc` (~42 de handlere)
- Macrourile `CREATE_HANDLER` / `CREATE_LOADER` simplifică declararea
### Handlere P10 Disponibile (libircu/)
| Token | Handler | Descriere |
|---|---|---|
| `B` | `msg_B` | BURST — sincronizare canale |
| `N` | `msg_N` | NICK — client nou / schimbare nick |
| `S` | `msg_S` | SERVER — server nou |
| `J` | `msg_J` | JOIN — intrare în canal |
| `L` | `msg_L` | PART — ieșire din canal |
| `Q` | `msg_Q` | QUIT — deconectare client |
| `M` | `msg_M` | MODE — schimbare moduri |
| `P` | `msg_P` | PRIVMSG — mesaj privat |
| `K` | `msg_K` | KICK — eliminare din canal |
| `G` / `GL` | `msg_G` / `msg_GL` | GLINE — ban global |
| `EB` | `msg_EB` | End of Burst |
| `EA` | `msg_EA` | End of Burst Acknowledge |
| `SQ` | `msg_SQ` | SQUIT — deconectare server |
| `AC` | `msg_AC` | ACCOUNT — autentificare |
| `T` | `msg_T` | TOPIC |
| `CM` | `msg_CM` | CLEARMODE |
| `W` | `msg_W` | WHOIS |
| `I` | `msg_I` | INVITE |
| `XQ` / `XR` | `msg_XQ` / `msg_XR` | Interogări/Răspunsuri extinse |
---
## 📊 Sistemul de Evenimente
### Evenimente de Rețea (definite în `include/events.h`):
| Eveniment | Descriere |
|---|---|
| `EVT_NICK` | Client nou conectat |
| `EVT_QUIT` | Client deconectat |
| `EVT_KILL` | Client eliminat (kill) |
| `EVT_CHNICK` | Schimbare de nick |
| `EVT_OPER` | Operator IRC |
| `EVT_NETJOIN` | Server nou conectat |
| `EVT_NETBREAK` | Server deconectat (netsplit) |
| `EVT_BURST_CMPLT` | Burst complet |
| `EVT_BURST_ACK` | Confirmare burst |
| `EVT_GLINE` / `EVT_REMGLINE` | Adăugare/Ștergere GLINE |
| `EVT_ACCOUNT` | Autentificare cont |
### Evenimente de Canal:
| Eveniment | Descriere |
|---|---|
| `EVT_JOIN` | Intrare în canal |
| `EVT_PART` | Ieșire din canal |
| `EVT_KICK` | Eliminare din canal |
| `EVT_TOPIC` | Schimbare topic |
| `EVT_CREATE` | Canal creat |
Modulele se înregistrează prin `RegisterEvent()` / `RegisterChannelEvent()` și primesc notificări prin `OnEvent()` / `OnChannelEvent()`.
---
## 🗄️ Baza de Date
### Arhitectura:
```
gnuworldDB (abstract) ← db/gnuworldDB.h — Interfață abstractă (Exec, isConnected)
└── pgsqlDB ← db/pgsqlDB.h — Implementare PostgreSQL (libpq)
```
### Modele SQL per Modul (exemplu: mod.cservice):
| Clasă | Fișier | Descriere |
|---|---|---|
| `sqlUser` | `mod.cservice/sqlUser.h` | Utilizator CService |
| `sqlChannel` | `mod.cservice/sqlChannel.h` | Canal înregistrat |
| `sqlLevel` | `mod.cservice/sqlLevel.h` | Nivel de acces utilizator-canal |
| `sqlBan` | `mod.cservice/sqlBan.h` | Ban pe canal |
Scripturile SQL de inițializare se găsesc în directorul `doc/` (`cservice.sql`, `ccontrol.sql`, etc.).
---
## ⚠️ Gestionarea Memoriei — Probleme Identificate
### Folosire exclusivă de `new`/`delete` brut
Codul nu utilizează **deloc** smart pointers (`std::unique_ptr`, `std::shared_ptr`). Toate alocările sunt manuale:
```cpp
// Exemple de alocare tipice:
iClient* theClient = new (std::nothrow) iClient(...);
Channel* theChan = new (std::nothrow) Channel(...);
ChannelUser* chanUser = new (std::nothrow) ChannelUser(theClient);
```
### Zone Critice de Dealocare:
| Locație | Ce se dezalocă |
|---|---|
| `xServer::doShutdown()` | Clienți locali, clienți de rețea, canale, servere, gline-uri, timere |
| `xNetwork::removeClient()` | `ChannelUser` asociați, canale goale |
| `xNetwork::removeServer()` | Toți clienții de pe serverul eliminat |
| `msg_Q::Execute()` | iClient la QUIT |
| `msg_B::Execute()` | Channel + ChannelUser la BURST (cu verificare de erori) |
### Potențiale Scurgeri de Memorie:
1. **`doShutdown()` — Iterare cu break prematur:** Buclele de cleanup iterează și fac `break` dacă containerul devine gol, dar sar elemente intermediare dacă mai sunt.
2. **`msg_B::parseBurstBans()`** — Vectorul de ban-uri este creat cu `st.size()` elemente goale *și* apoi se face `push_back`, rezultând elemente duplicate goale.
3. **`channelEventMap`** — Listele alocate cu `new` pentru evenimentele de canal trebuie dezalocate manual.
4. **Excepții negestionate** — Fără RAII, orice excepție între `new` și `delete` creează o scurgere.
---
## 📁 Structura Directoarelor
```
underchat-gnuworld/
├── src/ ← Codul sursă principal (main.cc, server.cc, Network.cc)
├── include/ ← Fișierele header principale
├── libircu/ ← Handlere protocol P10 (msg_*.cc — ~42 fișiere)
├── libgnuworld/ ← Biblioteci auxiliare (ConnectionManager, ELog, EConfig, Buffer)
├── db/ ← Stratul de bază de date (abstract + PostgreSQL)
├── mod.cservice/ ← Modulul CService (X) — managementul canalelor
├── mod.ccontrol/ ← Modulul CControl — administrare rețea
├── mod.chanfix/ ← Modulul ChanFix — reparare canale
├── mod.dronescan/ ← Modulul DroneScan — detectare boți
├── mod.nickserv/ ← Modulul NickServ — servicii de nick
├── mod.openchanfix/ ← Modulul OpenChanFix
├── mod.scanner/ ← Modulul Scanner
├── mod.snoop/ ← Modulul Snoop — monitorizare
├── mod.stats/ ← Modulul Stats — statistici
├── mod.cloner/ ← Modulul Cloner (testare)
├── mod.gnutest/ ← Modulul de test
├── mod.clientExample/ ← Exemplu de modul client
├── bin/ ← Fișiere de configurare (.conf, .sh)
├── doc/ ← Documentație și scripturi SQL
├── contrib/ ← Utilitare auxiliare
├── test/ ← Teste
└── tools/ ← Unelte de dezvoltare
```
---
## ⚙️ Compilare și Instalare
### Cerințe:
- **Compilator C++** (GCC / G++ — standard pre-C++11)
- **PostgreSQL** (libpq-dev)
- **libtool** (pentru încărcarea dinamică a modulelor)
- **autotools** (autoconf, automake)
### Pași de compilare:
```bash
./autogen.sh
./configure
make
make install
```
### Configurare:
Fișierele de configurare se găsesc în directorul `bin/`:
- `GNUWorld.example.conf.in` — Configurația principală a serverului
- `cservice.example.conf.in` — Configurația CService
- `ccontrol.example.conf.in` — Configurația CControl
- Alte fișiere `.conf.in` pentru fiecare modul
---
## 🔧 Recomandări de Modernizare
| Zonă | Starea Curentă | Recomandare |
|---|---|---|
| **Multiplexare I/O** | `select()` | Migrare la `epoll()` (Linux) sau `kqueue()` (BSD) |
| **Gestionare Memorie** | `new`/`delete` brut | Adoptare `std::unique_ptr` / `std::shared_ptr` (RAII) |
| **Standard C++** | Pre-C++11 | Migrare la C++17 minim |
| **Încărcare Module** | `libtool` (`lt_dlopen`) | `dlopen()` nativ sau C++20 modules |
| **Bază de Date** | PostgreSQL sincron | Query-uri asincrone (`PQsendQuery`) |
| **Parsare Stringuri** | `StringTokenizer` propriu | `std::string_view` + `std::regex` |
| **Logging** | `ELog` propriu / `log4cplus` opțional | Standardizare pe `spdlog` sau similar |
| **Testare** | Minimală | Adăugare teste unitare (Google Test / Catch2) |
---
## 📜 Licență
Acest proiect este distribuit sub licența **GNU General Public License v2** (GPL-2.0).
---
## 🤝 Contribuții
Proiect menținut de echipa **Underchat**. Contribuțiile sunt binevenite prin intermediul serverului Gitea:
🔗 **Repository:** [https://gitlab.back.ro/underchat/gnuworld.git](https://gitlab.back.ro/underchat/gnuworld.git)
---
*Generat pe baza analizei codului sursă — Martie 2026*