diff --git a/README.md b/README.md index fede52f9..b936ac41 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,22 @@ █████ ░░█████ █████████ █████ █████ █████ ░░█████████ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ``` -Internet Relay Chat project at 42 (a text-based communication protocol on the Internet)
+ +This is the main repository for Internet Relay Chat project at School 42
![Static Badge](https://img.shields.io/badge/-ft__IRC-blue?logo=42&logoColor=white) ![Static Badge](https://img.shields.io/badge/Language-C%2B%2B-blue) ![Static Badge](https://img.shields.io/badge/Protocole-IRC-blue) ![Static Badge](https://img.shields.io/badge/Doc-Doxygen-blue) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/jmkko/ft_IRC/c-cpp.yml) --- +## IRC protocol + +IRC is a text-based communication protocol over TCP/IP. It was first published in 1988 through RFC 1459, updated in 2000 by RFC 2810-2813. An IRC v3 was even proposed later on, which we didn't follow in this project. + +Established as a a successor to the Bulletin Board Systems, it grew popular the 1990s, before giving way to proprietary messaging services such as ICQ or MSN. + +Yet IRC community remains alive, with IRCNet, Libera Chat or OFTC being today's most popular IRC networks. -## ft_IRC - Subjet +## Subjet This project is about creating your own IRC server. You are required to develop an IRC server using the C++ 98 standard. @@ -27,140 +35,82 @@ Your executable will be run as follows: `./ircserv ` - Several IRC clients exist. You have to choose one of them as a reference. Your reference client will be used during the evaluation process. - Communication between client and server has to be done via TCP/IP (v4 or v6). ### Features -- authenticate, set a nickname, a username, join a channel, send and receive private messages using your reference client. -- All the messages sent from one client to a channel have to be forwarded to every other client that joined the channel. -- You must have operators and regular users. -- Commands that are specific to channel operators: - - KICK- Eject a client from the channel - - INVITE- Invite a client to a channel - - TOPIC- Change or view the channel topic - - MODE- Change the channel’s mode: - - i: Set/remove Invite-only channel - - t: Set/remove the restrictions of the TOPIC command to channel operators - - k: Set/remove the channel key (password) - - o: Give/take channel operator privilege - - l: Set/remove the user limit to channel +- message forwarding +- operator status +- Registration and User related commands + - `PASS` Authenticate a client + - `NICK` Change client nickname + - `USER` Register username and real name +- Messaging command + - `PRIVMSG` send a message to a client or a channel +- Channel related commands (some of which might be reserved to channel operators): + - `JOIN` Enter a channel + - `KICK` Eject a client from the channel + - `INVITE` Invite a client to a channel + - `TOPIC` Change or view the channel topic + - `MODE` Change the channel’s mode with following options + - `k`: Set/remove the channel key (password) + - `i`: Set/remove Invite-only channel + - `l`: Set/remove the user limit to channel + - `t`: Set/remove the restrictions of the TOPIC command to channel operators + - `MODE` Also change user mode with + - `o`: Give/take channel operator privilege ### Bonus - Handle file transfer. -- bot. +- Have a bot +### Extra features +We implemented extra commands not specifically asked in the subject +- PART +- QUIT +- PING +- WHO +- MOTD (message of the day, sent on successful registration) --- -## ft_IRC — Workflow de collaboration - -Ce dépôt est le dépôt principal du projet ft_IRC (école 42). -Nous travaillons en équipe de 3 étudiants, chacun sur un fork personnel de ce dépôt. -Toutes les contributions passent par des Pull Requests (PR) vers ce dépôt principal. - -### Organisation générale - -- `main` — branche stable, propre, toujours fonctionnelle. -- `feature/...` — branches créées dans chaque fork pour développer une fonctionnalité. - -### Mise en place du dépôt local (une seule fois) +## Collaboration workflow -1. Forker le dépôt principal -2. Cloner son fork personnel : -```bash -git clone git@github.com:MON-USERNAME/ft_IRC.git -cd ft_IRC -``` -3. Relier le dépôt principal en `upstream` : -```bash -git remote add upstream https://github.com/TON-USERNAME/ft_IRC.git -``` -4. Vérifier : -```bash -git remote -v -# doit montrer origin -> votre fork, et upstream -> dépôt principal -``` -### Workflow de développement -#### Créer une nouvelle fonctionnalité -```bash -# Se mettre à jour avant tout -git checkout main -git pull upstream main -git push origin main +- a main repository with individual forks +- issues to define main features or fixes +- each issue is worked upon on a dedicated branch +- a `main` branch that feature branches must synchronize with and where PR are submitted to +- we strived to follow basic commit message formatting -# Créer une branche de fonctionnalité -git checkout -b feature/ma_fonction -``` +cf. [wiki](https://github.com/jmkko/ft_IRC/wiki/Workflow) for details -#### Coder, puis : -```bash -git add . -git commit -m "Ajoute ma_fonction" -git push origin feature/ma_fonction -``` -#### Créer une Pull Request (PR) +## Quality and continuous integration workflow -1. Aller sur GitHub → votre fork -2. Sur votre branche → cliquer « Compare & pull request » -3. Vérifier : - - base : `TON-USERNAME/ft_IRC` → `main` - - compare : `MON-USERNAME/ft_IRC` → `feature/ma_fonction` -4. Créer la PR +### Norm and format -### Mettre à jour votre fork +- using `clang-format` +- based upon [LLVM coding rules](https://llvm.org/docs/CodingStandards.html)) -Avant de commencer une nouvelle fonctionnalité, synchronisez votre main local et votre fork : -```bash -git checkout main -git pull upstream main -git push origin main -```` -### Conventions de code +cf. [wiki](https://github.com/jmkko/ft_IRC/wiki/Norm) for details -- Commits clairs et concis (présent) - ✅ Ajoute la gestion du JOIN - ❌ J'ai codé join -- Branches : `feature/...` ou `fix/...` -- Jamais coder sur main -- Faire des PR petites et régulières +### Static analysis -### Tests +- using `clang-tidy` -We will strive to make different tests to prevent regressions while shipping new features. -This part of the project is not meant to be evaluated. -Therefore, we will use a more recent standard (c++20) and give a try at new features such as : -- lambdas -- introspection with type traits (ex: printing the name of an exception being thrown) +cf. [wiki](https://github.com/jmkko/ft_IRC/wiki/Static-Analysis) for details -Once implemented, each person implementing a new feature or a fix should be also responsible for the tests. +### Testing -- unit tests to check the output of a function, for normal and edge cases. -- integration test, based on a basic client, to check that a command or series or command produces the expected output from the server. +- a subproject `test` using c++20 +- integration tests to prevent regression +- no unit tests (as they might be redundant with integration tests most of the time : projet is not that big) -#### Assertions +cf. [wiki](https://github.com/jmkko/ft_IRC/wiki/Testing) for details -- `AssertUtils` contains utility functions to perform basic checks +### Continuous integration -#### Continuous integration and development +- using Github Actions pipelines : one for build and static checks, one for testing -If possible, we will try to add a Github Action workflow in order to check at each PR that -- the projects compiles -- the norm is respected (using a linter based on [LLVM code rules](https://llvm.org/docs/CodingStandards.html)) -- the codebase doesn't have potential bugs (using [CPPcheck](https://github.com/danmar/cppcheck), a static analysis checker) +## Architecture -### Résumé visuel -```bash - +-----------+ - | upstream | - | (principal)| - +-----+-----+ - ^ - | - +---------+----------+ - | | -+-----+-----+ +-----+-----+ -| fork 1 | | fork 2 | -| membre A | | membre B | -+-----------+ +-----------+ -``` +### Documentation -✨ Important : toujours synchroniser `main` avant de créer une branche de feature. ---- +cf [Doxygen generated documentation](https://jmkko.github.io/ft_IRC/html/index.html) -## ft_IRC — Stucture +### UML Class diagram ```mermaid --- @@ -389,136 +339,46 @@ classDiagram --- -## ft_IRC - Compétences acquises - -Ce projet est un projet synthèse du tronc commun de 42 qui nous a permis de mener -un travail en groupe et d'utiliser toutes les compétences apprises. - -- Projet en `C++` - Programmation orientée objet sur un projet complet -- Projet réseau - TCP , Socket , poll -- Protocol `IRC` - Syntaxe des messages -- Les `design pattern` - Nous avons utiliser `Singleton` et `Fabric` -- `Clang tidy` - Respect de règles de nommage et de qualité du code -- `Github` - Nous avons utiliser toutes les services disponibles - - `Fork` - Un repo principale + deux autres repos - - `Actions - Lancement Compilation du projet, puis d'un testeur - - `Rules` - Obligation d'avoir deux validation pour merge sur la main - - `Projet` - Roadmap, Issue (avec branch pour chacune) - - `Wiki` - - `Pages` - pour notre Documentation `Doxygen` -- `Doxygen` - Documentation automatique grace à des commentaires dans notre code -- `Mardown` et `Mermaid` - pour notre README +## Developed skills + +This project is a capstone for the 42 common core curriculum, allowing us to collaborate as a team and apply all the skills we’ve acquired throughout the program. + +| Skill/Tool | Description | +|--------------------------|-----------------------------------------------------------------------------| +| ![C++](https://img.shields.io/badge/-C++-00599C?logo=c%2B%2B&logoColor=white) | Object-oriented programming on a full-scale project | +| ![Networking](https://img.shields.io/badge/-Networking-0078D4?logo=network&logoColor=white) | TCP, Sockets, `poll` for event-driven I/O | +| ![IRC Protocol](https://img.shields.io/badge/-IRC%20Protocol-4EAA25?logo=irc&logoColor=white) | Message syntax and server-client communication | +| ![Design Patterns](https://img.shields.io/badge/-Design%20Patterns-9370DB?logo=design&logoColor=white) | Implemented `Singleton` and `Factory` patterns | +| ![Clang-Tidy](https://img.shields.io/badge/-Clang--Tidy-21759B?logo=llvm&logoColor=white) | Code quality and naming conventions | +| ![GitHub](https://img.shields.io/badge/-GitHub-181717?logo=github&logoColor=white) | Full GitHub workflow: Forks, Actions, Rules, Projects, Wiki, and Pages | +| ![Doxygen](https://img.shields.io/badge/-Doxygen-1E88E5?logo=doxygen&logoColor=white) | Automatic documentation from code comments | +| ![Markdown](https://img.shields.io/badge/-Markdown-000000?logo=markdown&logoColor=white) | Structured documentation with embedded diagrams using `Mermaid` | --- -## ft_IRC - Principales fonctions réseaux - -```c++ -/* -Crée un socket avec les paramètres passés. --family définit la famille du socket. Les valeurs principales sont AF_INET pour un socket IPv4, AF_INET6 pour un support IPv6. --type spécifie le type de socket. Les valeurs principales utilisées sont SOCK_STREAM pour TCP, SOCK_DGRAM pour UDP. --protocol définit le protocole à utiliser. Il sera dépendant du type de socket et de sa famille. Les valeurs principales sont IPPROTO_TCP pour un socket TCP, IPPROTO_UDP pour un socket UDP. -*/ - int socket(int family, int type, int protocol); - - //close socket - int close(int socket); -//Les fonctions de cette forme sont les fonctions Host/Home to Network . -//Elles servent à convertir les données numériques de la machine en données -//« réseau ». -short htons(short value); -long htonl(long value); -//Il s’agit des fonctions inverses des hton*. -short ntohs(short value); -long ntohl(long value); - /* - _socket est le socket à connecter. -server la structure représentant le serveur auquel se connecter. -serverlen est la taille de la structure server. socklen_t est un type spécifique aux plateformes UNIX et peut être un int ou unsigned int . Généralement un sizeof(server). - - L’appel à cette fonction est bloquant tant que la connexion n’a pas été effectuée. Autrement dit : si cette fonction retourne, c’est que votre connexion a été effectuée et acceptée par l’ordinateur distant. Sauf si elle retourne une erreur bien sûr. - - */ - int connect(int _socket, const struct sockaddr* server, socklen_t serverlen); - -//ex: -sockaddr_in server; -server.sin_addr.s_addr = inet_addr(const char* ipaddress); -server.sin_family = AF_INET; -server.sin_port = htons(int port); -/* -socket est le socket auquel envoyer les données. -datas les données à envoyer. -len est la taille maximale des données à envoyer en octets. -flags un masque d'options. Généralement 0. -*/ -int send(int socket, const void* datas, size_t len, int flags); -int recv(int socket, void* buffer, size_t len, int flags); -/* -La fonction bind est utilisée pour assigner une adresse locale à un socket. - -sckt est le socket auquel est assigné l'adresse. -name est la structure à assigner au socket. -namelen est la taille de cette structure -> sizeof. -Retourne SOCKET_ERROR -1 en cas d'erreur, 0 sinon. -*/ -int bind(SOCKET sckt, const struct addr* name, int namelen); -//ex -sockaddr_in addr; -addr.sin_addr.s_addr = INADDR_ANY; // indique que toutes les sources seront acceptées -addr.sin_port = htons(port); // toujours penser à traduire le port en réseau
-addr.sin_family = AF_INET; // notre socket est TCP -/* -sckt est le socket auquel les clients vont se connecter. -backlog est le nombre de connexions en attente qui peuvent être gérées. La valeur SOMAXCONN peut être utilisée pour laisser le système choisir une valeur correcte selon sa configuration. -*/ -int listen(SOCKET sckt, int backlog) ; -/* -Accepte une connexion entrante. - -sckt est le socket serveur qui attend les connexions. -addr recevra l'adresse du socket qui se connecte. -addrlen est la taille de la structure pointée par addr. -*/ -SOCKET accept(SOCKET sckt, struct sockaddr* addr, int* addrlen); -/* -Permet de récupérer l'adresse IP d'un socket, IPv4 ou IPv6, sous forme lisible. - -family est la famille du socket. -src le pointeur vers l'adresse du socket. -dst un pointeur vers un tampon où stocker l'adresse sous forme lisible. -size la taille maximale du tampon. -*/ -const char* inet_ntop(int family, const void* src, char* dst, socklen_t size); - -/*poll() permet de surveiller plusieurs file descriptors (sockets, fichiers, etc.) -pour détecter quand ils sont prêts pour certaines opérations (lecture, écriture, erreur) -sans bloquer. */ -#include -int poll(struct pollfd *fds, nfds_t nfds, int timeout); - -struct pollfd { - int fd; // File descriptor à surveiller - short events; // Événements à surveiller (INPUT) - short revents; // Événements qui se sont produits (OUTPUT) -}; +## Main networking functions +- socket creation and closure (`socket`) +- network byte order conversion (`htons`, `ntohs`, `htonl`, `ntohl`) +- connection management (`connect`, `bind`, `listen`, `accept`) +- data transmission (`send`, `recv`) +- event monitoring (`poll`) -``` +cf. [wiki](https://github.com/jmkko/ft_IRC/wiki/Networking-functions) for details --- -## ft_IRC - Source - Documentation +## Sources +### RFC +[RFC-145912](https://www.rfc-editor.org/rfc/rfc1459)
[RFC-2812](https://www.rfc-editor.org/rfc/rfc2812)
-[Server TCP](https://bousk.developpez.com/cours/reseau-c++/TCP/01-premiers-pas/)
+### Networking functions [Poll](https://devarea.com/linux-io-multiplexing-select-vs-poll-vs-epoll/)
[Network programing](https://beej.us/guide/bgnet/html/)
+[Server TCP](https://bousk.developpez.com/cours/reseau-c++/TCP/01-premiers-pas/)
+### Other interesting resources [Serveur IRC](https://www.cs.cmu.edu/~srini/15-441/S10/project1/pj1_description.pdf)
[Projet IRC](http://chi.cs.uchicago.edu/chirc/index.html)
[Design Pattern](https://refactoring.guru/fr/design-patterns/singleton)
[Mermaid](https://mermaid.js.org/syntax/classDiagram.html)
- - - diff --git a/includes/consts.hpp b/includes/consts.hpp index 58f2e04c..b68f469c 100644 --- a/includes/consts.hpp +++ b/includes/consts.hpp @@ -32,8 +32,10 @@ #define FORBIDEN_CHAR_BOT_PROMPT "|;" #define FORBIDEN_CHAR_CHAN_NAME "\x00\x07\x0D\x0A\x20\x2C\x3A" // NOLINT(clang-diagnostic-null-character) #define FORBIDEN_CHAR_CHAN_KEY "\x00\x09\x0A\x0B\x0C\x0D\x20" // NOLINT(clang-diagnostic-null-character) +#define FORBIDDEN_CHAR_SERVER_KEY "\x00\x09\x0A\x0B\x0D\x20" // NOLINT(clang-diagnostic-null-character) NUL, HT, LF, VT, CR, space #define FORBIDEN_CHAR_USER "\x00\x0A\x0D\x20\x40" // NOLINT(clang-diagnostic-null-character) #define NUMBER_FORB_CCNK 7 +#define NUMBER_FORB_CSK 6 #define NUMBER_FORB_CCU 5 #define VALID_CHAN_MODE_NOPARAM "\x69\x74" #define VALID_CHAN_MODE_PARAM "\x6B\x6C\x6F" diff --git a/includes/utils.hpp b/includes/utils.hpp index 4a40c53a..510133e9 100644 --- a/includes/utils.hpp +++ b/includes/utils.hpp @@ -151,6 +151,7 @@ class Utils static bool is_invalid_char_nick(char c); static bool is_invalid_char_user(char c); static bool is_invalid_char_key(char c); + static bool is_invalid_char_serverkey(char c); static bool is_not_digit(char c); }; // class utils diff --git a/srcs/utils.cpp b/srcs/utils.cpp index a023641c..41d3b34f 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -37,6 +37,13 @@ bool Utils::check_password(const std::string& s) LOG_SERVER.warning(std::string("password must be ") + TO_STRING(MIN_PASSWORD_LEN) + " long at least"); return false; } + for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) + { + if (Utils::is_invalid_char_key(*it)) { + LOG_w_SERVER("password must contain only ASCII chars in range A-Z, a-z, 0-9, !@#$%^&*(), \%x01-05, \%x07-08, \%x0C, and \%x0E-1F"); + return false; + } + } return true; } @@ -127,4 +134,5 @@ bool Utils::is_not_alpha_or_specialbnf(char c) { return (!std::isalpha(c) && !Ut bool Utils::is_invalid_char_nick(char c) { return (!std::isalnum(c) && !is_special_abnf(c)); } bool Utils::is_invalid_char_user(char c) { return (is_char_of(c, std::string(FORBIDEN_CHAR_USER, NUMBER_FORB_CCU))); } bool Utils::is_invalid_char_key(char c) { return (is_char_of(c, std::string(FORBIDEN_CHAR_CHAN_KEY, NUMBER_FORB_CCNK))); } +bool Utils::is_invalid_char_serverkey(char c) { return (is_char_of(c, std::string(FORBIDDEN_CHAR_SERVER_KEY, NUMBER_FORB_CSK))); } bool Utils::is_not_digit(char c) { return (!std::isdigit(c)); }