pax_global_header00006660000000000000000000000064150121307030014502gustar00rootroot0000000000000052 comment=204b7647cf68981f48ede9d5aa84c0c3801e17a9 swirc-3.5.5/000077500000000000000000000000001501213070300126435ustar00rootroot00000000000000swirc-3.5.5/CHANGELOG.md000066400000000000000000001275321501213070300144660ustar00rootroot00000000000000# Change Log # All notable changes to this project will be documented in this file. ## [3.5.5] - 2025-05-17 ## - **Added** a few micro-optimizations. - **Added** usage of `textBuf_emplace_back()` in _multiple_ places thus reduced duplication. - **Changed** `squeeze_text_deco_wide()` in order to make it more accurate for its task. - **Changed** the output of `/set`. When called without any arguments it lists all settings by columns. - **Changed** the program to kill the IRC server connection if event welcome is received more than once. Which for example can happen if ZNC loses its IRC server connection, and that causes troubles. - **Changed** the program to replace less than/greater than signs with `<` and `>` respectively, this in notifications. - **Fixed** a few Clang Tidy warnings. - **Fixed** building with `GNUMAKEFLAGS=--shuffle=reverse`. Reported by Lucas Nussbaum. Thanks! - **Fixed** conversion from type `A` -> `B`, possible loss of data. Multiple occurrences. - **Replaced** calls to `getenv()` with `getenv_s()`. - **Replaced** calls to `sscanf()` with `sscanf_s()`. ### Windows ### - **Added** a help file with documentation. - **Changed** to static linking of the exe. - **Replaced** calls to: - `wcsncpy()` with `wcsncpy_s()`. - `wcstombs()` with `wcstombs_s()`. - `Swirc.wxs`: converted v3 source to v4 source. - **Upgraded** to [LibreSSL](https://www.libressl.org/) 4.1.0 ## [3.5.4] - 2025-02-22 ## - **Added** lowercase `/znc` subcommands used for auto complete. - **Added** translations. - **Changed** the program set the DCC and FTP download dirs differently. - **Did** code improvements: - Atomic vars. - Null checks. - Refactoring. - Usage of `auto` for iterators. - Usage of `nullptr`. - `...` ### Windows ### - **Fixed** `/znc JumpNetwork`. ## [3.5.3] - 2025-02-08 ## - **Added** tab completion for: - `/dcc` - `/ftp` - **Changed** the program to squeeze text decoration in notification messages. - **Fixed** option `notifications`. (Did not take effect on private messages). - **Fixed** so that `/znc JumpNetwork` works. (It didn't until now due to the lack of reinitialization of the IRC module). ## [3.5.2] - 2025-02-01 ## - **Added** command `/ctcp` - **Added** command `/ftp` - **Added** subcommand `ls` to `/dcc` which can be used to list the contents of the upload/download directories. - **Added** the following options: - `ctcp_userinfo` (string) - `ftp_host` (string) - `ftp_port` (integer, defaults to 21) - `ftp_user` (string) - `ftp_pass` (string) - `ftp_upload_dir` (string) - **Added** the key combination `CTRL+w` (List all windows) - **Added** the possibility to switch between windows by just entering the reference number, i.e. `/1`, `/2`, `/3`... - **Added** usage of `emplace_back()`. Performance. Multiple occurrences. - **Changed** `is_valid_filename()` to disallow `..` - **Changed** the program to beep on certain window activity. - **Fixed** `-Wshadow` warnings - **Fixed** a resource leak found by Coverity. - **Fixed** uncontrolled data used in path expressions. ## [3.5.1] - 2024-11-09 ## - **Added** command `/announce`. - **Added** command-line option `-X` (Disable all IRCv3 extensions) - **Added** event `CHGHOST` (IRCv3.) - **Added** option `account_tag` (bool). - **Added** option `chghost` (bool). Send a notification when clients change their username or host. Defaults to **yes**. - **Added** option `extended_join` (bool). - **Added** the `NEW` and `DEL` CAP subcommands. And implicitly enabled the `cap-notify` extension. - **Added** the following SASL auth mechanisms: - `SCRAM-SHA-1` - `SCRAM-SHA-512` - **Changed** the program to: - Init/deinit Windows sockets once. - Not terminate the DCC server on IRC disconnect. It is Client-to-Client which means file transfers can continue. IRC is only used for the negotiation. - Output a reason if `SSL_connect()` fails. - Output a reason if `tls_server::get_accept_bio()` fails. - **Did** code improvements: - Added default constructors - Reduced duplication - Reduced scope of variables - Removed redundant checks - Replaced macros for constants - Usage of initializer lists - `...` - **Enabled** `account_notify` by default. - **Enabled** more colors for BSD and macOS. - **Fixed** cases of uncontrolled allocation sizes. - **Fixed** potential signed integer overflows. ### Windows ### - **Upgraded** to [LibreSSL](https://www.libressl.org/) 4.0.0 ## [3.5.0] - 2024-09-30 ## - **Added** command `/wholeft` (alias `/wl`.) - **Added** option `notifications` (bool). Turns notifications on/off. - **Added** new translations. - **Added** support for netjoin/netsplit. Few IRC server software support the batches which were added in 3.4.7. This release comes with its own implementation. - **Improved** `xwcwidth()`. I.e. added more full width character ranges, and switched to a better search algorithm. ## [3.4.9] - 2024-07-28 ## - **Added** event 468 (`ERR_INVALIDUSERNAME`) - **Added** usage of `nullptr`. - **Changed** the program to not start reconnecting on event 433 (`ERR_NICKNAMEINUSE`) if a connection is in progress. - **Fixed** [CWE-190](https://cwe.mitre.org/data/definitions/190.html) bugs found by Coverity. - **Fixed** a few potential data races found by TSan. - **Fixed** a null pointer dereference. - **Improved** the thread safety. ## [3.4.8] - 2024-06-04 ## - **Added** the following commands: - `/voice` - `/devoice` - **Added** tab completion for: - `/deop` - `/kick` - `/kickban` - `/op` - **Added** translations. - **Changed** the program to: - Output the number of messages in a batch. - Output the number of nicks in a netjoin/netsplit plus the server hostnames. - **Did code improvements**: - Explicitly specified the size of multiple arrays (MISRA rule, found by PVS-Studio). - Fixed octal constants (MISRA rule, found by PVS-Studio). - Fixed redundant checks and expressions. - Harnessed the power of templates and defined `ARRAY_SIZE()` differently for C++. - Marked variables with `const` and `immutable_cp_t` respectively. - Reduced the scope of variables. - Variable initializations. - ... - **Fixed** the begin/end messages of a batch to not end up in the status window. - **List** all commands dynamically in the output of `/help`, i.e. calculate the cols/rows with respect to the current window size. ### Windows ### - **Fixed** a case of truncation from `int` to `wchar_t`. - **Upgraded to**: - [Curl](https://curl.se/) 8.8.0 - [LibreSSL](https://www.libressl.org/) 3.9.2 ## [3.4.7] - 2024-03-12 ## - **Added** command `/dcc` - **Added** event `BATCH` (IRCv3). The following types were added: - `chathistory` - `netjoin` - `netsplit` - `znc.in/playback` - **Added** tab completion for: - `/mode` - **Added** the following config options: - `batch` (bool) - `dcc` (bool) - `dcc_cipher_suite` (string) - `dcc_own_ip` (string) - `dcc_port` (int) - `dcc_upload_dir` (string) - `multi_prefix` (bool) - Did code optimizations: - Measure string length once, found by [PVS-Studio](https://pvs-studio.com/en/pvs-studio/). - ... - Enabled `iconv_conversion` by default. - **Fixed** an off-by-one error in the IRC module. The previous program versions weren't really affected however. - **Fixed** building on any setup where Ncurses is built with opaque types. - **Fixed** new [Coverity](https://scan.coverity.com/) defects - **Fixed** the precision of the error log size in KB. - Improved C++ exception handling. Among other things, handle if 'new' throws. - Renamed option `ircv3_server_time` to `server_time` and enabled it by default. ## [3.4.6] - 2023-11-05 ## - **Added** adoption of SPDX-tags for the distributed manual pages - **Added** better detection of server Wallops - **Added** command-line option `-S` (Force TLS) - **Added** event 249 (`RPL_STATSDEBUG`) - **Added** extensive use of the `CPPFLAGS` make macro - **Added** handling of empty user@host combinations in Wallops messages - **Added** handling of znc messages. (Made them look like notices and appear in the active window.) - **Added** protection for bad filenames for IRC logs - **Added** translations - **Changed** the way of recognizing a user mode versus channel mode - **Did** code improvements - **Fixed** occurrences of hardcoded function names in messages - **Fixed** typos in manual pages - **Improved** C++ exception handling - **Limited** notification messages in length ### Windows ### - **Upgraded to**... - [Curl](https://curl.se/) 8.4.0 - [LibreSSL](https://www.libressl.org/) 3.8.2 ## [3.4.5] - 2023-09-14 ## - **Added** command-line option `-W` - **Added** tab completion for: - `/cs`, `/chanserv` - `/ns`, `/nickserv` - `/squery` - **Added** the following commands: - `/admin` - `/die` - `/gline` - `/info` - `/ison` - `/kline` - `/rehash` - `/restart` - `/servstats` - `/wallops` - **Added** the following events: - 211 (`RPL_STATSLINKINFO`) - 212 (`RPL_STATSCOMMANDS`) - 216 (`RPL_STATSKLINE`) - 219 (`RPL_ENDOFSTATS`) - 242 (`RPL_STATSUPTIME`) - 256 (`RPL_ADMINME`) - 257 (`RPL_ADMINLOC1`) - 258 (`RPL_ADMINLOC2`) - 259 (`RPL_ADMINEMAIL`) - 303 (`RPL_ISON`) - 371 (`RPL_INFO`) - 374 (`RPL_ENDOFINFO`) - **Added** translations - **Did** code improvements ### Windows ### - **Fixed** a bug which caused the program to exit silently without any message. This when turning logging on for a window. The solution was to replace `fprintf()` with `fprintf_s()`. ## [3.4.4] - 2023-07-29 ## - **Added** a man page that describes how to write themes. - **Added** and made use of a pointer validation function. - **Added** improved unicode support in the **printtext** and **readline** modules. - **Added** usage of a binary search algorithm. - **Added** validation of nicknames before they go into the names hash table. - **Allowed** the names htbl modify API to alter names even if the list is incomplete. (But only in ICB mode.) - **Changed** the file suffix for theme files from `.the` to `.thm`. (`.the` looks too much like the English word "the".) - **Closed** an open stream before exit - **Defined** noreturn for C++ in `fallbackattrs.h` - **Fixed** hardcoded function names in error messages - **Fixed** unchecked return values - And more... ## [3.4.3] - 2023-03-10 ## - **Added** code improvements: - **Added** constructors/destructors - **Added** improved algorithms - **Added** usage of initializer lists - **Added** usage of new/delete - **Deleted** unused includes - ... - **Added** event `320` (the same as `275` and `671`, whois ssl) - **Added** MULTIPLE new translations ## [3.4.2] - 2023-02-22 ## - **Added** usage of typedefs (`STRING`, `CSTRING`, `errno_t`, ...) - **Fixed** printtext() bugs due to not checking if the wide character passed to `wcrtomb_s()` or `wcrtomb()` is a null character. ## [3.4.1] - 2023-02-18 ## - **Changed** the requirements for checking if a connection is lost. (The change avoids flooding the server with PING commands better.) - **Improved** algorithms. - **Optimized** commonly used functions for speed. - **Replaced** C-style casts in C++. ### Windows ### - **Fixed** missing DLLs. Unfortunately some DLLs were missing after adding the dependency on Hunspell. Since I am running a dev environment I had them my PATH and therefore didn't notice it. ## [3.4.0] - 2023-02-10 ## - **Added** a SOCKS proxy client - **Added** command `/fetchdic` - **Added** destruction of null bytes on receive where appropriate - **Added** spelling support with Hunspell - **Added** the following config options: - `socks` (bool) - `socks_atyp` (int) - `socks_host` (string) - `socks_port` (string) - `spell` (bool) - `spell_lang` (string) - `spell_syswide` (bool) - **Added** usage of `emplace_back()` - **Fixed** possible crashes ### Windows ### - **Fixed** launch by toast. (COM appends the `-Embedding` flag to the args passed, which caused troubles.) - It is no longer necessary to enable the option "Use Unicode UTF-8 for worldwide language support", because this is now done in the exe-manifest, only for Swirc. - **Upgraded to**... - [Curl](https://curl.se/) 7.87.0 - [LibreSSL](https://www.libressl.org/) 3.6.2 ## [3.3.9] - 2023-01-07 ## - **Added** micro optimizations - **Added** rules for nickname highlighting - **Added** SASL auth mechanism: **external** - **Added** support for the **GNU Hurd OS** - **Added** tab completion for: - `/connect` - `/time` - `/version` - **Added** the following commands: - `/qbot` - `/stats` - **Added** the following config options: - `cmd_type_prot` (bool) - `ctcp_reply` (bool) - `qbot_host` (string) - `sasl_x509` (string) - **Improved** tab completion for: - `/msg` - `/notice` - `/whois` ### Linux ### - **Fixed** building on riscv64. Contributed by [Eric Long](https://github.com/hack3ric) - thanks! ## [3.3.8] - 2022-11-27 ## - **Added** an ident protocol daemon (which is OFF by default) - **Added** and made use of `printf_and_free()` - **Added** exception handling - **Added** tab completion for `/theme` - **Added** the following config options: - `awaymsgs_in_privconv` (bool) - `identd` (bool) - `identd_fakenames` (bool) - `identd_port` (int) - **Added** the following events: - 413 (`ERR_NOTOPLEVEL`) - 414 (`ERR_WILDTOPLEVEL`) - 415 (`ERR_BADMASK`) - **Added** usage of `__func__` - **Added** usage of `printtext_print()` - **Deleted** commented-out code - **Deleted** unused includes - **Did code refactoring** - **Fixed** display of IPv6 addresses in event 338 - **Fixed** verification of the X509 certificate presented by the peer - **Made** const arrays static const - **Made** improvements to the following files: - `commands/theme.c` - `network.cpp` - `printtext.cpp` - **Moved** `squeeze_text_deco()` to a separate file - Reduced VA args in printtext calls ### Windows ### - Upgraded to [LibreSSL](https://www.libressl.org/) 3.6.1 ## [3.3.7] - 2022-10-22 ## - **Added** code to the following scripts: - `posixshell/link_with_gnu_libidn.sh` - `posixshell/link_with_libnotify.sh` - **Added** preprogrammed IRC network [OFTC](https://www.oftc.net/) - **Added** usage of `swircpaths.h`, a header which is automatically generated during the build process. - **Fixed** a case of a possibly negative array subscript ### Windows ### - **Fixed** a bug that made Swirc fail to start with error `renamed executable`. This was due to that `_get_pgmptr()` signaled success but stored an empty string, which only occurred on some Windows setups. (Reported by [cpkio](https://github.com/cpkio) - thanks!) ## [3.3.6] - 2022-10-05 ## - **Added** cryptographic functions - **Added** subcommand `passwd_s` to `/sasl` which can be used to securely store encrypted SASL passwords in `swirc.conf`. The encryption technique practice the use of **AES** and **SHA**. - **Added** tab completion for - `/msg` - `/notice` - `/sasl` - **Changed** the program behavior to - jump between ICB and IRC mode depending on the port number - **not** create core dump files if `NDEBUG` is defined at compile time - **not** echo the server password on input (command line flag `-p`) - **Fixed** a bug so if a connection attempt fails it's possible to reconnect again after `/disconnect`. - **Fixed** code duplication - **Fixed** insufficient out of range check, off-by-one, in command `/unignore`. - Made small improvements - **Added** usage of C++17 fallthrough attr - **Added** usage of `PATH_MAX` - **Added** usage of `__func__` instead of hardcoding it (for example in error messages) - **Fixed** redundant checks - Reduced scope of variables - Switched to usage of 'sizeof' in `BZERO()` calls - ... - Prevented the config hash table from being paged to the swap area. - **Rewrote** `commands/sasl.c` in C++ - **Updated** preprogrammed IRC servers - **Added** port numbers and server descriptions - **Added** the [IRCNow](https://ircnow.org/) network - **Deleted** the [Blitzed](http://blitzed.org/) network ## [3.3.5] - 2022-07-28 ## - **Added** command `/cap` - **Added** config option `iconv_conversion` (bool) - **Added** config option `mouse_events` - **Added** new translations - **Added** usage of `noexcept` and installed a terminate handler - **Changed** the startup screen to show the current language - **Fixed** C style casts in C++ - **Fixed** SASLprep by switching to usage of `stringprep_profile()`. Prep now works on Windows too! - **Fixed** memory leaks - **Fixed** unhandled exceptions - Improved memory handling - Improved the contents of `/help` - Completed translating everything to Swedish - **Made** config option `mouse` change take affect at once - **Made** improvements to `events/cap.c` - **Made** small optimizations - **Made** the dot mo files available for read operations with `unveil()` - Updated the TODO :-) ### Windows ### - Added a GNU bundle containing: 1. [gettext runtime](https://www.gnu.org/software/gettext/) 0.21 2. [libiconv](https://www.gnu.org/software/libiconv/) 1.17 3. [libidn](https://www.gnu.org/software/libidn/) 1.38 - Fixed missing DLL file `libiconv-2.dll`. I had it in my PATH so I didn't notice it. ## [3.3.4] - 2022-05-04 ## - **Added** a TLS server (to be used in a future version) - **Added** config option `mouse` (defaults to **no**) - **Added** config option `server_cipher_suite` - **Added** creation of OpenSSL scripts - **Added** event 335 (`RPL_WHOISBOT`) - **Added** global hashfunctions - **Added** preprogrammed IRC network [AlphaChat](https://www.alphachat.net/) - **Added** scrolling using the mouse - **Added** theme item: - `nicklist_my_nick_color` - `whois_bot` - **Added** translations - **Changed** the statusbar to display readline stats - **Converted** - `commands/services.c` -> `commands/services.cpp` - `config.c` -> `config.cpp` - **Defined** `SLASH` once - **Defined** and made use of: - `UNUSED_PARAM()` - `UNUSED_VAR()` - `colorarray_t` - `g_beginthread_failed` - `g_received_welcome` - `usage_t` - **Fixed** bugs discovered with protocol fuzzing - Handle PRIVMSGs from "my" server - Handle truncation of ICB messages, i.e. allow longer messages than `ICB_MESSAGE_MAX`. - Improved scrolling - Made refactoring to multiple files - Moved the names hash table modify API - Print PRIVMSGs to irc channels from users that aren't in them. (No lookup error.) - Reduced code duplication - Reformatted and reindented files ### Windows ### - **Added** GNU LibIntl 0.19.8.1 which is part of [gettext](https://www.gnu.org/software/gettext/) - [Curl](https://curl.se/) 7.83.0 - [LibreSSL](https://www.libressl.org/) 3.5.2 ## [3.3.3] - 2021-11-27 ## - **Added** event `531`. Undocumented in the RFC. (You cannot send CTCPs to this user whilst they have the +T `u_noctcp` mode set.) - **Added** event `728` and `729`. Undocumented in the RFC. Channel Quiet List. - **Added** function `xstrnlen()` and made use of it - **Added** new [Libera Chat](https://libera.chat/) servers - **Added** null checks - **Deleted** unused includes - **Fixed** macro redefinitions - **Fixed** unchecked return values - Made improvements to the following files: - `commands/invite.c` - `commands/notice.cpp` - `commands/services.c` - `events/invite.cpp` - `events/wallops.cpp` - `assertAPI.c` - `errHand.c` - `icb.c` - Added status message `Idle-Mod`. - Added status message `Timeout`. - `readlineTabCompletion.c` - Refactoring - ... - Reformatted and reindented files: - `commands/me.c` - `commands/msg.c` - `commands/squery.cpp` - `commands/znc.cpp` - `events/account.cpp` - `events/banlist.cpp` - `events/servlist.cpp` - `statusbar.cpp` - `titlebar.c` - ... ### Debian ### - Added patches ### Windows ### - Upgraded to [LibreSSL](https://www.libressl.org/) 3.4.2 ## [3.3.2] - 2021-10-17 ## - **Added** command line option `-j` - **Added** function `getuser()` and made use of it - **Added** null checks - **Added** the following command aliases: - `/j` (`/join`) - `/p` (`/part`) - **Added** the following commands: - `/ignore` - `/unignore` - **Fixed** unchecked return values - Made improvements to the following files: - `commands/connect.c` - `events/channel.cpp` - `events/misc.cpp` - `events/notice.cpp` - `events/privmsg.cpp` - `events/welcome.cpp` - `events/whois.cpp` - `dataClassify.c` - `log.c` - `nestHome.c` - Reformatted and reindented files - **Rewrote** the `/join` and `/part` commands in C++ - **Updated** the translations ### Windows ### - **Fixed** terminal resizing. (Increasing the size is ok.) - The [PDC](https://pdcurses.org/) library has been rebuilt with the option `UTF8=Y` which makes PDCurses ignore the system locale. So for Windows computers running Swirc UTF-8 is now forced. This means you should enable the option **Use Unicode UTF-8 for worldwide language support**. In order to do so under Windows 10: 1. Language preferences 2. Administrative language settings 3. Change system locale 4. Check the option and click OK ## [3.3.1] - 2021-09-03 ## - **Added** command line option `-R` (Disable TLS/SSL peer verification) - **Added** configuration flag `--without-libintl` - **Added** key F1 (Output help) - Configuration option `joins_parts_quits` now defaults to **NO** - Documented F5 - F10 in command `/colormap` - **Fixed** "no text to send" bug - Made improvements to the following files: - `events/auth.c` - Translated parts to **German**, **Finnish**, **French** and **Swedish**. ## [3.3.0] - 2021-06-27 ## - **Added** function `errdesc_by_last_err()` and made use of it - **Added** key F3 (scroll nicklist up) and F4 (scroll nicklist down) - **Added** new modes and options for TLS/SSL connections - **Added** nicklist - **Added** null checks - **Added** preprogrammed network name [libera](https://libera.chat/) - **Added** support for partial writes in `net_ssl_send()` - **Added** theme item `nicklist_nick_color` - **Added** theme item `nicklist_privilege_color` - **Added** theme item `nicklist_vline_color` - Asserted that the program is terminated correctly - **Defined** and made use of `addrof()` - **Defined** and made use of `g_textdeco_chars` - Deleted `ToastActivator_i.c` - Deleted `ToastActivator_p.c` - Deleted `dlldata.c` - Deleted command `/n` - Explicitly set client mode for TLS/SSL connections - **Fixed** "use after free" bug in `/cycle` - **Fixed** a bug in `/quit` that resulted in SIGPIPE due to calling `SSL_shutdown()` on an already shutdown socket. - **Fixed** the behavior of `net_ssl_recv()` by checking the condition of `SSL_pending()`. - **Fixed** unchecked return values - Made improvements to the following files: - `io-loop.c` - `irc.c` - `network-openssl.c` - `network.cpp` - `pthrMutex.c` - `readline.c` - `readlineAPI.c` - `sig-unix.c` - `sig-w32.c` - `vcMutex.c` - `window.c` - Modified the scrolling behavior - Moved defines - Reformatted and reindented files - Renamed functions and patterns - **Rewrote the printtext module in C++ and made multiple improvements**! - Upgraded to: - [Curl](https://curl.se/) 7.77.0 - [LibreSSL](https://www.libressl.org/) 3.3.3 ## [3.2.7] - 2021-03-13 ## - Added checking of `term_is_too_small()` - Added null checks - Defined `g_conversion_failed` and made use of it - Defined `g_time_error` and made use of it - Fixed a bug that could lead to null pointer comparison - Fixed limited prompt length - Fixed stricter checking of channel names - Fixed unchecked return values and made code improvements - Improved automatic resizing - Reduced code duplication - Rewrote `event_invite()` and thus fixed a vulnerability that imply that a malicious IRC server message could cause a crash. (Reported by Michael Ortmann.) - Rewrote `events/names.c` using C++ and made various improvements ## [3.2.6] - 2021-02-17 ## - Added logging of program name and PID for debug/error messages - Added tab completion for: - `/help` - `/znc` - Fixed multiple non-ANSI function declarations - Fixed multiple sign-compare warnings - Fixed unchecked return values - Improved the configure script - Splitted the configure script into smaller parts - Made functions that yet weren't declared at file scope to be - WIN32: fixed behavior in `/quit` - WIN32: upgraded to [LibreSSL](https://www.libressl.org/) 3.2.4 ## [3.2.5] - 2020-10-09 ## ### Added ### - Command - `/servlist`: used to list services currently connected to the network. - `/squery`: used for communication with irc network services. - `/znc` (for communication with [znc](https://www.znc.in/)) - Event - 234 (`RPL_SERVLIST`) - 235 (`RPL_SERVLISTEND`) - 493\. Undocumented in the RFC. ngIRCd: `You must share a common channel with [...]`. ### Changed ### - `network-openssl.c`: `suite_secure`: `TLSv1.3:TLSv1.2+AEAD+ECDHE:TLSv1.2+AEAD+DHE` - From obsolete `ctime()` calls to instead use `strftime()` - To seed the random number generator using a new approach. (The deterministic RNG isn't used in a security context anyway.) ### Fixed ### - Command-line option `-P`. (It disabled SASL authentication but didn't end IRCv3 capability negotiation.) ### News ### - [LibreSSL](https://www.libressl.org/) 3.1.4 ## [3.2.4] - 2020-04-08 ## ### Added ### - Improved documentation - More colors for Windows and a synchronized colormap - Make target (`check`) for running unittests - Command-line option - `-C` (Do not change color definitions) - `-P` (Permanently disable SASL authentication) - Tab completion for - `/query` - `/whois` ### Changed ### - Display logging in statusbar ### Fixed ### - A possible out-of-bounds read/write array operation ## [3.2.3] - 2020-03-22 ## ### Added ### - Code improvements ### Changed ### - Multiple events to use try-catch blocks - Stopped using `free_not_null()` as `free()` handles null ### Fixed ### - Color 11 ## [3.2.2] - 2020-03-17 ## ### Added ### - **Multicolor support**. For terminals supporting minimum 256 colors. - Command - `/colormap` - `/echo` - Tab completion for `/set` ### Fixed ### - Color (Note: For terminals supporting minimum 16 colors) - Grey - Light Grey - Multiple occurrences where: - Pointer parameters could be declared as pointing to const - Tab completion bugs ## [3.2.1] - 2020-03-01 ## ### Added ### - **Code improvements** - Fetching of the Mozilla CA certificate store (in PEM format) for Windows builds - Improved help and documentation - Updated setver script :-) ### Fixed ### - Multiple occurrences where: - Pointer parameters could be declared as pointing to const - Previously assigned value to a variable hasn't been used ## [3.2.0] - 2020-02-13 ## ### Added ### - **Logging**. Which can be toggled on/off with CTRL+L per window and works while IRC connected. The default is off for all windows. - **Tab completion** - Better detection for connection loss ### Changed ### - For C++, prefer `const_cast` and `static_cast` respectively, instead of C style casts. ### Fixed ### - Possibly lossy type conversions - Readline bugs ## [3.1.1] - 2019-12-22 ## ### Fixed ### - Possible readline deadlock ### News ### - [Curl](https://curl.haxx.se/) 7.67.0 - [LibreSSL](https://www.libressl.org/) 3.0.2 ## [3.1.0] - 2019-12-06 ## ### Added ### - Command-line option - `-4` (Use IPv4 addresses) - `-6` (Use IPv6 addresses) - `-d` Debug logging - Event - 464 (`ERR_PASSWDMISMATCH`) - Preprogrammed network name: - [afternet](https://www.afternet.org/) - [blitzed](http://blitzed.org/) - SASL auth mechanism [SCRAM-SHA-256](https://tools.ietf.org/html/rfc7677) ### Changed ### - Made command `/say` available for ICB ### Fixed ### - Command-line option `-p` (which queries the user for a connection password). It has been broken for some time but I haven't noticed it until now. - Connection to a IPv6-only host. (Reported by Ross Richardson). - Reconnection with connection password ### Updated ### - Help for command - `/connect` - `/sasl` ## [3.0.0] - 2019-10-31 ## ### Added ### - **SUPPORT FOR THE ICB PROTOCOL** - Command - `/beep` - `/boot` - `/group` - `/kill` - `/passmod` ### Changed ### - Error messages (in order to improve them) ### Fixed ### - Better reconnection - Improved functions - Multiple occurrences of `Conditional expression should have essentially Boolean type` - Spelling errors ## [2.9.0] - 2019-09-10 ## ### Added ### - **Code improvements** - Cleaner auto-generation of configs and themes - More color pairs ### Changed ### - Tell if a command is unknown - Tell if away from keyboard ### Deleted ### - `say()` ### Fixed ### - Colorized statusbar ### News ### - [PDCurses](https://pdcurses.org/) 3.9 ## [2.8.0] - 2019-07-16 ## ### Added ### - Command - `/ban` + `/unban` - `/kickban` - `/op` + `/deop` - Event - 696\. Undocumented in the RFC. (You must specify a parameter for the key mode). - 698\. Undocumented in the RFC. (Channel ban list does not contain `...`). - Preprogrammed network name [anonops](https://www.anonops.com/). ### Changed ### - `RPL_BOUNCE`->`RPL_ISUPPORT` - `SW_NORET`->`NORETURN` - Don't shutdown irc connection on runtime error in: - `event_join()` - `event_kick()` - `event_part()` ### Deleted ### - `swirc_wprintw()` due to deprecation ### Fixed ### - Reported by [MAGA](https://github.com/MakeItGreatAgain): - Issues with protocol colon escaping - Reproducible crash from protocol fuzzing - `/cycle` fails when a channel key is in use - Reproducible crashes from protocol fuzzing ## [2.7.2] - 2019-05-26 ## ### Added ### - **A rewritten interpreter** - **Linux**: hardened builds - Code improvements - Event - 416\. Officially undocumented. (output too large, truncated) - Improved help for command - `/mode` - `/theme` - `/who` ### Changed ### - Install-destination of the manual pages ### Fixed ### - Missing null checks ## [2.7.1] - 2019-04-26 ## ### Added ### - A banner in WiX ### Changed ### - Names module to use DJB2 hashing (for more efficient results) ### Fixed ### - Impact of a possibly uninitialized variable - Possible resource leaks - Unchecked return values ### News ### - [LibreSSL](https://www.libressl.org/) 2.9.1 ## [2.7.0] - 2019-04-20 ## ### Added ### - Command - `/cleartoasts` - Improved help for command - `/join` - `/set` - Option - `beeps` ### Changed ### - Configure script messages - Default values for the following options: - `nickname` - `real_name` - `username` - UNIX: Produce a stripped executable - WIN32: Compile with O2 ### Deleted ### - Option - `disable_beeps` ### News ### - [Curl](https://curl.haxx.se/) 7.64.1 - [LibreSSL](https://www.libressl.org/) 2.8.3 ## [2.6.1] - 2019-03-30 ## ### Added ### - Algorithm improvements - The possibility of desktop notifications with the help of configure flag `--with-libnotify` ## [2.6] - 2019-03-21 ## ### Added ### - Code improvements - Event - **PONG** - 451 (`ERR_NOTREGISTERED`) - Option - `joins_parts_quits` (Show JOIN/PART/QUIT events?) - `reconnect_backoff_delay`: The number of seconds that should be added to each reconnect attempt (0-99) - `reconnect_delay`: Seconds to consume before the first reconnect attempt (0-999) - `reconnect_delay_max`: Maximum reconnect delay in seconds (0-999). Regardless of the other related reconnect settings. - `reconnect_retries`: If the IRC connection is lost, how many attempts should be performed to get the connection working again before giving up? ### Changed ### - **Lowered recv/send timeouts during connection establishment for faster processing** - Don't require irc connection for command `/disconnect` - Scrolling behavior with the help of `arc4random()` ### Fixed ### - Bugs in command - /kick - /part - Typos ### News ### - [PDCurses](https://pdcurses.sourceforge.io/) 3.8 ## [2.5] - 2018-12-21 ## ### Added ### - Extended help for ALL commands ### Changed ### - Toast activated launch argument ### Deleted ### - Windows: `SpawnMessageLoop()` happened to be a *mistake* and was therefore deleted! ### Fixed ### - **Cases of missing deinitializations regarding names in a channel!** - **Thread termination!** ### News ### - Readline is now non-blocking during connection to a server! - Reorganized changelog :-) - Windows: **Improved installer** ## [2.4] - 2018-11-11 ## ### Added ### - Event AWAY - Option - `away_notify` - `invite_notify` ### Changed ### - Rewrote - `event_cap()` - `event_privmsg()` - ... - **MULTIPLE** functions to use try-catch blocks ### Fixed ### - **A dependency on uninitialized memory!** - Memory leaks - Off-by-one error ### News ### - Curl 7.62.0 - LibreSSL 2.8.2 ## [2.3] - 2018-10-24 ## ### Added ### - A `swirc.conf` manual page - Command /oper - Event - `ACCOUNT` - `WALLOPS` - 381 (`RPL_YOUREOPER`) - 491 (`ERR_NOOPERHOST`) - 908 (`RPL_SASLMECHS`) - Option - `account_notify` - `auto_op_yourself` ### Changed ### - **Option `ircv3_server_time` is now working** ### Deleted ### - Option - `encoding` - `recode` ### News ### - **OpenBSD**: Uses `unveil()` if available - **Windows**: Passes tests of Windows App Certification Kit ## [2.2] - 2018-09-30 ## ### Added ### - Option - `ircv3_server_time` **currently a no-op** - `nickname_aliases`. A space separated list of nickname aliases which are used, in addition to the default nickname, to highlight a message if it matches any of the aliases given by this setting. - Several code improvements - Unit tests for - `is_alphabetic()` - `is_numeric()` - Windows 10 toast notifications ### Changed ### - Certain error messages - Output Swirc homepage on CTCP version reply ### Fixed ### - Possibly lossy conversions - Some functions that should be static, but weren't... ### News ### - **Windows**: MSI installer ## [2.1] - 2018-05-21 ## ### Added ### - Command /set - New build system for - UNIX - Windows - Unit tests for - `int_diff()` - `int_sum()` - `size_product()` - `squeeze_text_deco()` - `sw_strcat()` - `sw_strcpy()` - Use of `size_product()` in multiple places ### Changed ### - **Linux**: fix cflags with pkg-config - Name of function - `Strcolor` to `strColor` - `Strdup_printf` to `strdup_printf` - `Strdup_vprintf` to `strdup_vprintf` - `Strfeed` to `strFeed` - `Strings_match_ignore_case` to `strings_match_ignore_case` - `Strings_match` to `strings_match` - Send `/whois` on `spawn_chat_window()` when on air and the window isn't an irc channel. - The printtext color map ### Fixed ### - Building for NetBSD ### News ### - Curl 7.60.0 - LibreSSL 2.7.3 - PDCurses 3.6 ## [2.0] - 2018-02-24 ## ### Added ### - Command alias - /cs (for chanserv) - /ns (for nickserv) - Event - 465 (`ERR_YOUREBANNEDCREEP`) - 466 (`ERR_YOUWILLBEBANNED`) - `size_to_int()` which asserts given conversion isn't lossy - `xfopen()` which is a wrapper for `fopen` and `fopen_s` - `xmbstowcs()` which is a wrapper for `mbstowcs` and `mbstowcs_s` - `xstrerror()` which is a wrapper for `strerror_s` and `strerror_r` ### Changed ### - **Windows**: always produce UTF-8 output ### Fixed ### - Errors in the license (more specifically in the disclaimer) ## [1.9] - 2017-11-12 ## ### Added ### - Key - CTRL+a (move to beginning of line) - CTRL+e (move to end of line) ### Changed ### - **Windows**: Machine type from x86 to x64 which means that a **64-bit executable** will be built next time ### Fixed ### - **Windows**: Library issues causing the newly introduced command /sasl to work improperly ## [1.8] - 2017-10-27 ## ### Added ### - Command /sasl - Event - 462 (`ERR_ALREADYREGISTRED`) - 486 (You must log in with services to message this user) - 901 (`RPL_LOGGEDOUT`) - 902 (`ERR_NICKLOCKED`) - 903 (`RPL_SASLSUCCESS`) - 904 (`ERR_SASLFAIL`) - 905 (`ERR_SASLTOOLONG`) - 906 (`ERR_SASLABORTED`) - 907 (`ERR_SASLALREADY`) - Key - F11 (close window) - F12 (close all private conversations) - Many small improvements - Option - `sasl` - `sasl_mechanism` - `sasl_password` - `sasl_username` - Support for SASL authentication during registration process - Tor hidden service to freenode servers ### Fixed ### - **Browsing the command history caused the program to freeze if the next/prev command exceeded a certain count!** ## [1.7] - 2017-08-12 ## ### Added ### - Command /theme for management of themes on-the-fly - Enhancements to the printtext module - Event 442 `ERR_NOTONCHANNEL` - Theme item `slogan` - WIN32: use of function `MessageBox()` in the errHand module ### Changed ### - Theme item `sw_ascLogotype_color` to `logo_color` - Turned `nodelay()` OFF which reduces CPU usage - WIN32: theme item `term_use_default_colors` will always be set to NO ### Fixed ### - **Buggy behavior of `squeeze_text_deco()`!** ## [1.6] - 2017-05-24 ## ### Added ### - Event - 479 (Illegal channel name) - 716 and 717 (Error responses for CTCP requests) ### Changed ### - The statusbar to display channel modes ### Fixed ### - Occurrences of - possible dereferences of null pointers - usage plain integers as null pointers - **Handle if server sends names for a particular channel twice or more** - `net_ssl_check_hostname`: memory leak. ## [1.5] - 2017-03-05 ## ### Added ### - Config option `cipher_suite`. Valid values are - **secure** - **compat** - **legacy** - **insecure** - Config option `hostname_checking` - Event 461 (`ERR_NEEDMOREPARAMS`) - Function `X509_check_host` for implementations that lacks support for it - Via /connect it's now possible to connect, only by specifying a particular IRC network, preprogrammed networks are: - [efnet](http://www.efnet.org/) - [freenode](https://freenode.net/) - [ircnet](http://www.ircnet.org/) - [quakenet](https://www.quakenet.org/) - [undernet](http://www.undernet.org/) ### Changed ### - ASCII logo - Connection timeout to 45 seconds - Set `ssl_verify_peer` to **YES** even on WIN32 - Statusbar to display user modes - Title for status window (to Swirc homepage) ### Fixed ### - A command history feature bug - Improved TLS/SSL security - Key enter for Windows - Usage for /who. (Parameter mask is mandatory.) ## [1.4] - 2017-02-03 ## ### Added ### - Broadcasting of channel activity if our nickname matches certain patterns - Capability of reading a... - CTCP TIME reply (requested by /time) - CTCP VERSION reply (requested by /version) - Command - /close - /time - /version - Event - 492\. **Official name**: `ERR_NOSERVICEHOST`. **On InspIRCd**: User does not accept CTCPs. - 500\. Undocumented in the RFC. (Only a server may modify the +r user mode) - WIN32: A resource script ### Changed ### - If server port is 6697 automatically set TLS/SSL on ### Deleted ### - Code duplication ### Fixed ### - A bogus behavior in /connect - Better performance - CTCP VERSION reply bug - Save nickname, username and real name automatically to the config if customized by the command-line. - `event_notice`: handle an empty *user@host* combination ## [1.3] - 2017-01-14 ## ### Added ### - Broadcasting of window activity for private messages - Command - /cycle - /rules - /who - Command line option `-i` for ICB mode (Internet Citizen's Band). Plans are to support the protocol but it's **not implemented yet**! - Event - 232, 308 and 309 (numeric replies for /rules -- undocumented in the RFC) - 315 (`RPL_ENDOFWHO`) - 352 (`RPL_WHOREPLY`) - 421 (`ERR_UNKNOWNCOMMAND`) - 435\. Undocumented in the RFC. (Cannot change nickname while banned on channel). - 437 (`ERR_UNAVAILRESOURCE`) - 444 (`ERR_NOLOGIN`) - 477 (`ERR_NOCHANMODES`) - 484 (`ERR_RESTRICTED`) ### Changed ### - **Attention**: Don't verify peer per default on WIN32 on SSL connections due to unable to get local issuer certificate. - Connection timeout to 15 seconds - The prompt for the status window to nothing... - `NAMES_HASH_TABLE_SIZE` to 4500 - `event_notice`: output IRC prefix + params on error. ### Fixed ### - **Instead of using slots for chunks of names** use a linked list! - **Printtext**: switch off all terminal attributes during indentation. - Handle empty *user@host* combination for - `event_join()` - `event_kick()` - `event_part()` - `event_privmsg()` - `event_quit()` - `event_topic_chg()` - Signal 11 when connecting to [Slack](https://slack.com/). ## [1.2] - 2016-09-22 ## ### Added ### - A function to show error log size on startup - Checks for printflike functions - Checks in order to prohibit the program from running with superuser privileges (aka root) - Command history feature - Logging to the error log for settings that are detected with invalid values ### Changed ### - **GNU/Linux and OS X**: compile using optimization level 2. - Command line option `-p`. Instead of taking an argument it will now query the user for a password during connection to an IRC server. - The name of function `PrintAndFree()` to `print_and_free()` and its behavior. Plus multiple commands to use it. - `event_names_print_all()`: adjust the column-width with respect to the longest name in each column. ### Fixed ### - **Printtext**: A problem with converting a wide character to a multibyte sequence due to EILSEQ - A bug regarding that mode +q has different meanings on different server software (IRCd's) - Possible crash due to resizing the terminal while connection is in progress ## [1.1+] - 2016-08-31 ## ### Changed ### - `NAMES_HASH_TABLE_SIZE` to 6500 ### Deleted ### - Duplicated code ### Fixed ### - Absence of *An* macro in the man page - Readline: `session_destroy`: Get rid of possibly issued warning regarding: discards qualifiers from pointer target type. - `src/unix.mk`: provide a standard default target **all** - io-loop: Get rid of zero-length printf format string warnings. (They weren't harmful in the first place.) ## [1.1] - 2016-08-27 ## ### Added ### - Command - /banlist - /chanserv - /exlist - /ilist - /nickserv - Additional data to the manual page - Event - 324 (`RPL_CHANNELMODEIS`) - 329\. Undocumented. Channel date of creation. - 346-349 (numeric replies to /exlist and /ilist) - 367 (`RPL_BANLIST`) - 368 (`RPL_ENDOFBANLIST`) - 487\. Undocumented in the RFC. An error meaning that /msg target *ChanServ* or *NickServ* is no longer supported (in favor of **/chanserv** and **/nickserv**). - On OpenBSD: restrict system operations by using pledge() if it's available. - Window scrolling capabilities! ### Changed ### - **/msg**: fail if recipient is *ChanServ* or *NickServ*. (Use of the commands **/chanserv** and **/nickserv** should be considered instead.) - Interpreter: handle string truncation when copying an identifier or argument - Look of the output produced by /list - Only call `unget_wch(MY_KEY_RESIZE)` after sleeping for a requested interval. This possibly prevents the program from crashing or the terminal from being hung up due to a "resize attack". - Printtext: replaced unbounded `sw_sprintf` calls with `sw_snprintf` - `textbuffer_size_absolute` now defaults to 1500 ### Fixed ### - An issue - ...regarding the maintenance of channel statistics - ...where the cursor was left at the statusbar (after updating it) - Visible usage of /list - `net_ssl_send`: signed-unsigned mix with relational ## [1.0] - 2016-08-13 ## ### Added ### - Support for multiple encodings: - UTF-8 - ISO-8859-1 - ISO-8859-15 - Command /list - Event - 321-323 (numeric replies for /list) - 334\. Undocumented. At Undernet, it prints out usage describing parameters for the LIST command. - 341 (`RPL_INVITING`) - 412 (`ERR_NOTEXTTOSEND`) - 467 (`ERR_KEYSET`) - 471-474 - 481 (`ERR_NOPRIVILEGES`) - 742 (MODE cannot be set due to channel having an active MLOCK restriction policy). ATM not documented in the RFC. ### Changed ### - The default theme. Color codes that consisted of only one digit were changed to use two digits in some cases. For example: ^C3 changed to ^C03, ^C5 changed to ^C05, etc. Why? Consider a function call like printtext(..., "%s`one or more digits`", Theme(`primary_color`)). If the primary color, in this case, consists of only one digit and is directly followed by `one or more digits` the result is: one digit is lost and a random color will be displayed. ### Fixed ### - As a fallback, instead of printing out "Printtext Internal Error", continue to convert characters even if some are lost due to an invalid multibyte sequence. - Interpretation of color codes in the printtext module. For example: ^C123 didn't display '3' before as it should. ## [1.0b] - 2016-07-30 ## - FIRST OFFICIAL VERSION OF SWIRC! swirc-3.5.5/CONTRIBUTORS.md000066400000000000000000000010231501213070300151160ustar00rootroot00000000000000# CONTRIBUTORS # - [Emanuel Berg](https://dataswamp.org/~incal/) - Feedback and ideas. - Michael Ortmann - Bug report - [Sebastian Nielsen](https://github.com/sebastiannielsen) - Feedback and ideas. Join his IRC server (address: **sebbe.eu**). The main channel is *#sebastian* but also try *#english*. - [Staffan Thomén](https://github.com/sthomen) - **New build system for the UNIX environment** - Portability issues for NetBSD - [Stefan Wallin](https://github.com/StefanWallin) - Strategically - Feedback swirc-3.5.5/License.rtf000066400000000000000000000041111501213070300147370ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Tahoma;}{\f1\fnil\fcharset2 Symbol;}} {\*\generator Riched20 10.0.17134}\viewkind4\uc1 \pard\f0\fs32\lang1053 License agreement\fs20\par Swirc Internet Relay Chat client\par Copyright (c) Markus Uhlin. All rights reserved.\par \par \b Redistribution and use in source and binary forms, with or without\par modification, are permitted provided that the following conditions are\par met:\par \b0\par \pard{\pntext\f1\'B7\tab}{\*\pn\pnlvlblt\pnf1\pnindent0{\pntxtb\'B7}}\fi-360\li720 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\par \pard\par \pard{\pntext\f1\'B7\tab}{\*\pn\pnlvlblt\pnf1\pnindent0{\pntxtb\'B7}}\fi-360\li720 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\par \pard\par \pard{\pntext\f1\'B7\tab}{\*\pn\pnlvlblt\pnf1\pnindent0{\pntxtb\'B7}}\fi-360\li720 Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\par \pard\par THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\par "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\par LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\par A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\par HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\par INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\par BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\par OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\par ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR\par TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\par USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\par DAMAGE.\par \par } swirc-3.5.5/Makefile000066400000000000000000000017031501213070300143040ustar00rootroot00000000000000include options.mk CPPFLAGS += -Isrc/include PREFIX ?= /usr/local all: main include src/commands/build.mk include src/events/build.mk include src/build.mk main: $(TGTS) .c.o: $(E) " CC " $@ $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< .cpp.o: $(E) " CXX " $@ $(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< include tests/recompile.mk $(OBJS): $(SRC_DIR)include/swircpaths.h check: $(SRC_DIR)include/swircpaths.h $(OBJS) $(RM) $(RECOMPILE) $(Q) strip --strip-symbol=main $(SRC_DIR)main.o $(MAKE) -Ctests include maketargets/install.mk include maketargets/tidy.mk clean: $(E) " CLEAN" $(RM) $(COMMANDS_DIR)*.c.smatch $(RM) $(EVENTS_DIR)*.c.smatch $(RM) $(SRC_DIR)*.c.smatch $(RM) $(SRC_DIR)include/swircpaths.h $(RM) $(OBJS) $(RM) $(TGTS) $(RM) -R cov-int $(RM) -R swirc.analyze $(RM) PVS-Studio.log $(RM) report.tasks $(RM) strace_out $(RM) swirc.html $(RM) swirc.static.html $(MAKE) -Cpo clean $(MAKE) -Ctests clean swirc-3.5.5/Makefile.vc000066400000000000000000000127041501213070300147160ustar00rootroot00000000000000# -*- mode: makefile; -*- !include options.w32.mk BUILD_LOG = build.log # C preprocessor flags CPPFLAGS = $(CPPFLAGS)\ -Icurl-$(CURL_VERSION)/include\ -Ignu-bundle-$(GNU_BUNDLE_DATE)/include\ -Ihunspell-$(HUNSPELL_VERSION)/include\ -Ilibressl-$(LIBRESSL_VERSION)/include\ -Isrc/include DEST = c:\out\swirc-$(MACHINE) # Locale location LOCLOC = LC_MESSAGES\swirc.mo PRODUCT_VERSION = 3.5.5 REVISION = all: main !include src/commands/build.w32.mk !include src/events/build.w32.mk !include src/build.w32.mk !include htmlhelp/build.mk main: $(TGTS) .c.obj: $(E) ^ ^ CC^ ^ ^ ^ ^ ^ $@ $(Q) $(CC) $(CFLAGS) $(CPPFLAGS) -c -Fo$*.obj $< 1>>$(BUILD_LOG) .cpp.obj: $(E) ^ ^ CXX^ ^ ^ ^ ^ $@ $(Q) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -Fo$*.obj $< 1>>$(BUILD_LOG) # XXX: Add the MINGW DLLs? (Hunspell) DIST_DEPS = swirc.exe\ "curl-$(CURL_VERSION)\$(MACHINE)\libcurl.dll"\ "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libcharset).dll"\ "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libiconv).dll"\ "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libidn).dll"\ "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libintl).dll"\ "hunspell-$(HUNSPELL_VERSION)\$(MACHINE)\$(NAME_libhunspell).dll"\ "pdcurses-$(PDCURSES_VERSION)\$(MACHINE)\pdcurses.dll"\ "src\trusted_roots.pem" dist: $(DIST_DEPS) $(HTMLHELP_DIR)swirc.chm ! if exists($(DEST)) rmdir /s /q $(DEST) ! endif mkdir $(DEST) mkdir $(DEST)\de\LC_MESSAGES mkdir $(DEST)\fi\LC_MESSAGES mkdir $(DEST)\fr\LC_MESSAGES mkdir $(DEST)\sv\LC_MESSAGES copy "curl-$(CURL_VERSION)\$(MACHINE)\libcurl.dll" $(DEST) copy "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libcharset).dll" $(DEST) copy "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libiconv).dll" $(DEST) copy "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libidn).dll" $(DEST) copy "gnu-bundle-$(GNU_BUNDLE_DATE)\$(MACHINE)\$(NAME_libintl).dll" $(DEST) copy "hunspell-$(HUNSPELL_VERSION)\$(MACHINE)\$(NAME_libhunspell).dll" $(DEST) copy "hunspell-$(HUNSPELL_VERSION)\$(MACHINE)\$(DLL_libgcc)" $(DEST) copy "hunspell-$(HUNSPELL_VERSION)\$(MACHINE)\$(DLL_libstdcpp)" $(DEST) copy "hunspell-$(HUNSPELL_VERSION)\$(MACHINE)\$(DLL_libwinpthread)" $(DEST) copy "hunspell-en-us\en_US.aff" $(DEST) copy "hunspell-en-us\en_US.dic" $(DEST) copy "libressl-$(LIBRESSL_VERSION)\$(MACHINE)\$(DLL_crypto)" $(DEST) copy "libressl-$(LIBRESSL_VERSION)\$(MACHINE)\$(DLL_ssl)" $(DEST) copy "pdcurses-$(PDCURSES_VERSION)\$(MACHINE)\pdcurses.dll" $(DEST) copy "src\trusted_roots.pem" $(DEST) copy "swirc.exe" $(DEST) copy $(HTMLHELP_DIR)swirc.chm $(DEST) copy swirc-locales-$(LOCALES_SNAP)\de\$(LOCLOC) $(DEST)\de\LC_MESSAGES copy swirc-locales-$(LOCALES_SNAP)\fi\$(LOCLOC) $(DEST)\fi\LC_MESSAGES copy swirc-locales-$(LOCALES_SNAP)\fr\$(LOCLOC) $(DEST)\fr\LC_MESSAGES copy swirc-locales-$(LOCALES_SNAP)\sv\$(LOCLOC) $(DEST)\sv\LC_MESSAGES ALG = SHA256 MSI_ARCH = x64 SIGNTOOL_FLAGS = -f "C:\CERTS\SwircDevelopmentTeam.pfx"\ -fd $(ALG)\ -tr "http://timestamp.digicert.com"\ -td $(ALG)\ -v setupfile: dist Swirc.wxs signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(DLL_libgcc)" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(DLL_libstdcpp)" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(DLL_libwinpthread)" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(NAME_libcharset).dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(NAME_libhunspell).dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(NAME_libiconv).dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(NAME_libidn).dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(NAME_libintl).dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\libcurl.dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(DLL_crypto)" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\$(DLL_ssl)" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\pdcurses.dll" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" "$(DEST)\swirc.exe" wix build -arch $(MSI_ARCH) -culture "en-us" -ext WixToolset.UI.wixext Swirc.wxs -out "Swirc-$(PRODUCT_VERSION)$(REVISION).msi" signtool sign $(SIGNTOOL_FLAGS) -p "$(PASSWD)" \ "Swirc-$(PRODUCT_VERSION)$(REVISION).msi" clean: $(E) ^ ^ CLEAN $(RM) "*.msi" $(RM) "*.wixpdb" $(RM) "src\*.obj" $(RM) "src\commands\*.obj" $(RM) "src\events\*.obj" $(RM) "src\trusted_roots.pem" $(RM) $(BUILD_LOG) $(RM) $(TGTS) $(RM) Swirc.wixobj $(RM) curl-$(CURL_VERSION).cab $(RM) gnu-bundle-$(GNU_BUNDLE_DATE).cab $(RM) hunspell-$(HUNSPELL_VERSION).cab $(RM) hunspell-en-us.cab $(RM) libressl-$(LIBRESSL_VERSION).cab $(RM) pdcurses-$(PDCURSES_VERSION).cab $(RM) swirc-locales-$(LOCALES_SNAP).cab $(RM) swirc.res ! if exists(cov-int) rmdir /s /q cov-int ! endif ! if exists(cppcheck-data) rmdir /s /q cppcheck-data ! endif ! if exists(curl-$(CURL_VERSION)) rmdir /s /q curl-$(CURL_VERSION) ! endif ! if exists(gnu-bundle-$(GNU_BUNDLE_DATE)) rmdir /s /q gnu-bundle-$(GNU_BUNDLE_DATE) ! endif ! if exists(hunspell-$(HUNSPELL_VERSION)) rmdir /s /q hunspell-$(HUNSPELL_VERSION) ! endif ! if exists(hunspell-en-us) rmdir /s /q hunspell-en-us ! endif ! if exists(libressl-$(LIBRESSL_VERSION)) rmdir /s /q libressl-$(LIBRESSL_VERSION) ! endif ! if exists(pdcurses-$(PDCURSES_VERSION)) rmdir /s /q pdcurses-$(PDCURSES_VERSION) ! endif ! if exists(swirc-locales-$(LOCALES_SNAP)) rmdir /s /q swirc-locales-$(LOCALES_SNAP) ! endif ! if exists(tmp) rmdir /s /q tmp ! endif $(Q) cd po $(Q) $(MAKE) -f Makefile.vc clean $(Q) cd .. swirc-3.5.5/Package.appxmanifest000066400000000000000000000022771501213070300166270ustar00rootroot00000000000000 swirc-3.5.5/README.md000066400000000000000000000115521501213070300141260ustar00rootroot00000000000000# README # ![Swirc Logo](https://www.nifty-networks.net/swirc/gfx/swirc-royal-110x110.png) [![Coverity Scan Build Status](https://scan.coverity.com/projects/18215/badge.svg)](https://scan.coverity.com/projects/uhlin-swirc) [![CodeQL](https://github.com/uhlin/swirc/actions/workflows/codeql.yml/badge.svg)](https://github.com/uhlin/swirc/actions/workflows/codeql.yml) [![macOS](https://github.com/uhlin/swirc/actions/workflows/macos.yml/badge.svg)](https://github.com/uhlin/swirc/actions/workflows/macos.yml) ## What is Swirc? ## Swirc is a BSD licensed, console based and lightweight ICB and IRC client written in C/C++, whose goals are to be portable and secure. [Official Swirc homepage](https://www.nifty-networks.net/swirc/) ## Program options ## usage: swirc [-46?CPRSXdipv] [-W password] [-c server[:port]] [-j join] [-n nickname] [-r rl name] [-u username] [-x config] -4 Use IPv4 addresses only -6 Use IPv6 addresses only -?, --help Output help -C Do not change color definitions -P Permanently disable SASL authentication -R Disable TLS/SSL peer verification -S Force TLS -W Equal effect as flag 'p' but non-interactive -X Disable all IRCv3 extensions -c Connect to IRC server -d Debug logging -i Turn on Internet Citizen's Band mode -j A comma-separated list of channels to join -n Online nickname -p Query for server password (for private servers) -r Your real name -u Your username -v, --version Output Swirc version -x Config file ## SAST Tools ## [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. ## Cloning ## To clone the repository use [Git](https://git-scm.com). $ git clone https://github.com/uhlin/swirc.git ## Building ## ### Framework ### Swirc currently depends on: * [Curl](https://curl.haxx.se/libcurl/) * [GNOME libnotify](https://wiki.gnome.org/) * [GNU gettext](https://www.gnu.org/software/gettext/) * [GNU libiconv](https://www.gnu.org/software/libiconv/) * [GNU libidn](https://www.gnu.org/software/libidn/) * [Hunspell](https://hunspell.github.io/) * [Ncurses](https://www.gnu.org/software/ncurses/ncurses.html) with wide character support * [OpenSSL toolkit](https://www.openssl.org/) Which means that on for example a Debian GNU/Linux system you need to install these packages before building: # apt install gettext libcurl4-openssl-dev libhunspell-dev libidn11-dev libncursesw5-dev libssl-dev And on Mac OS X, provided that [Homebrew](http://brew.sh/) is installed, issue: $ brew install hunspell $ brew install libressl Prompts that begin with a hash(#) symbolizes that the command shall be executed as root, while prompts that begin with a dollar sign ($) symbolizes that the command shall be executed as a normal user. #### Void Linux #### # xbps-install -S gettext-devel hunspell-devel libcurl-devel libidn-devel libnotify-devel ncurses-devel openssl-devel ### Building for the UNIX environment ### On the BSDs and GNU/Linux the configure script will per default generate make definitions that expects that the C compiler [GCC](https://gcc.gnu.org/) is installed on your system. A make utility must also be present. Regarding Mac OS X I suggest that you install [Xcode](https://developer.apple.com/xcode/). Due to certain circumstances I no longer can confirm that building for OS X works. $ cd /path/to/swirc $ ./configure $ make #### Configuration options #### The following options can be passed to the configure script: - `--with-libnotify`: Enable support for desktop notifications - `--without-libiconv`: Build without GNU libiconv - `--without-libidn`: Build without GNU libidn - `--without-libintl`: No internationalization #### Install #### 1. Installing it under `/usr/local`: $ sudo make install 2. Installing it under `/home/user` without the translations (in which case you also should've passed `--without-libintl` to the configure script): $ PREFIX=/home/user make install-no-lc-msgs ### Building for Windows ### To build Swirc for Windows you must have [Visual Studio](http://www.visualstudio.com/). So, fire up the command prompt for [Visual Studio](http://www.visualstudio.com/) where the needed tools (the compiler, etc.) are loaded into the environment. The regular command prompt won't work. Then: cd c:\path\to\swirc nmake -f Makefile.vc *Done!* To make a distribution of Swirc use: nmake -f Makefile.vc dist ## Cleaning ## Examples: $ make clean $ nmake -f Makefile.vc clean swirc-3.5.5/SECURITY.md000066400000000000000000000007071501213070300144400ustar00rootroot00000000000000# Security Policy # ## Supported Versions ## | Version | Supported | | ------- | ------------------ | | 3.5.5 | :heavy_check_mark: | | 3.5.4 | :heavy_check_mark: | | < 3.5.4 | :x: | ## Reporting a Vulnerability ## To report a vulnerability send an e-mail to [security@nifty-networks.net](mailto:security@nifty-networks.net). The development of Swirc isn't funded in any way. But I do my best to keep it safe and sensible. swirc-3.5.5/Swirc.wxs000066400000000000000000000227461501213070300145100ustar00rootroot00000000000000 swirc-3.5.5/TODO.md000066400000000000000000000001461501213070300137330ustar00rootroot00000000000000# TODO # - Complete translations for: 1. German 2. Finnish 3. French (Create a pull request.) swirc-3.5.5/VERSION_INFO000066400000000000000000000001461501213070300145270ustar00rootroot00000000000000MAJOR_VERSION=3 MINOR_VERSION=5 PATCHLEVEL=5 VERSION=${MAJOR_VERSION}.${MINOR_VERSION}.${PATCHLEVEL} swirc-3.5.5/configure000077500000000000000000000060771501213070300145640ustar00rootroot00000000000000#!/bin/sh # Swirc configure script # # SPDX-FileCopyrightText: Copyright 2016-2025 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause MAKE_DEF_FILE=options.mk . "posixshell/check_etext.sh" . "posixshell/check_strcasestr.sh" . "posixshell/fix_cflags.sh" . "posixshell/link_with_gnu_libidn.sh" . "posixshell/link_with_gnu_libintl.sh" . "posixshell/link_with_hunspell.sh" . "posixshell/link_with_libiconv.sh" . "posixshell/link_with_libnotify.sh" . "posixshell/set_common.sh" . "posixshell/os_BSD.sh" . "posixshell/os_GNU.sh" . "posixshell/os_LINUX.sh" . "posixshell/os_LINUX_suncc.sh" . "posixshell/os_MAC.sh" . "posixshell/os_NETBSD.sh" _sanitize_address=0 _sanitize_thread=0 _fuzz_mode=0 _with_libnotify=0 _without_libiconv=0 _without_libidn=0 _without_libintl=0 print_help () { echo "" echo "Configuration options:" echo "" echo " --with-libnotify Enable support for desktop notifications" echo " --without-libiconv Build without GNU libiconv" echo " --without-libidn Build without GNU libidn" echo " --without-libintl No internationalization" echo "" if [ "$(whoami)" = "maxxe" ]; then echo " --sanitize-address" echo " --sanitize-thread" echo " --fuzz-mode" echo "" echo "ASAN_OPTIONS=\"log_path=asan.log\"" echo "TSAN_OPTIONS=\"log_path=tsan.log\"" fi } for arg in "$@"; do case "$arg" in "--sanitize-address") _sanitize_address=1 ;; "--sanitize-thread") _sanitize_thread=1 ;; "--fuzz-mode") _fuzz_mode=1 ;; "--help" | "-?" | "-h") print_help exit 0 ;; "--with-libnotify") _with_libnotify=1 ;; "--without-libiconv") _without_libiconv=1 ;; "--without-libidn") _without_libidn=1 ;; "--without-libintl") _without_libintl=1 ;; *) ;; esac done cat <$MAKE_DEF_FILE E = @echo Q = @ RM = @rm -f SLASH_SYM = / EOF case "$(uname -s)" in "Darwin") os_MAC ;; "FreeBSD") os_BSD ;; "GNU") os_GNU ;; "Linux") if [ "$1" = "suncc" ]; then os_LINUX_suncc else os_LINUX fi fix_cflags if [ "$(uname -m)" = "riscv64" ]; then cat <>$MAKE_DEF_FILE LDLIBS += -latomic EOF fi ;; "NetBSD") os_NETBSD ;; "OpenBSD") os_BSD if [ "$(uname -m)" = "sparc64" ]; then cat <>$MAKE_DEF_FILE LDLIBS += -lstdc++fs EOF fi ;; *) echo "OS not supported!" exit 1 ;; esac check_etext check_strcasestr if [ ${_sanitize_address} -eq 1 ]; then cat <>$MAKE_DEF_FILE CFLAGS += -fsanitize=address CXXFLAGS += -fsanitize=address EOF fi if [ ${_sanitize_thread} -eq 1 ]; then cat <>$MAKE_DEF_FILE CFLAGS += -fsanitize=thread CXXFLAGS += -fsanitize=thread EOF fi if [ ${_fuzz_mode} -eq 1 ]; then cat <>$MAKE_DEF_FILE CPPFLAGS += -DIRCFUZZ_MODE=1 EOF fi if [ ${_with_libnotify} -eq 1 ]; then link_with_libnotify fi if [ ${_without_libiconv} -eq 0 ]; then link_with_libiconv fi if [ ${_without_libidn} -eq 0 ]; then link_with_gnu_libidn fi if [ ${_without_libintl} -eq 0 ]; then link_with_gnu_libintl # ${MAKE:-make} -Cpo clean ${MAKE:-make} -Cpo fi link_with_hunspell if [ -f "$MAKE_DEF_FILE" ]; then echo "configure: $MAKE_DEF_FILE successfully created!" else echo "configure: fatal error" exit 1 fi swirc-3.5.5/errHand.cfg000066400000000000000000000010131501213070300147020ustar00rootroot00000000000000 true true true true swirc-3.5.5/gen-hdr.sh000077500000000000000000000013231501213070300145250ustar00rootroot00000000000000#!/bin/sh if [ $# -ne 1 ]; then echo "bogus number of args" exit 1 fi HDRPATH=src/include/swircpaths.h PREFIX=$1 shift cat <${HDRPATH} #ifndef _SWIRCPATHS_H_ #define _SWIRCPATHS_H_ #define SWIRC_BTD_PATH "${PREFIX}/share/locale" #define SWIRC_ICON_PATH "${PREFIX}/share/swirc/swirc-royal.png" #define HUNSPELL_PATH "${PREFIX}/share/hunspell" #define LC_MSGS_DE "${PREFIX}/share/locale/de/LC_MESSAGES/swirc.mo" #define LC_MSGS_FI "${PREFIX}/share/locale/fi/LC_MESSAGES/swirc.mo" #define LC_MSGS_FR "${PREFIX}/share/locale/fr/LC_MESSAGES/swirc.mo" #define LC_MSGS_SV "${PREFIX}/share/locale/sv/LC_MESSAGES/swirc.mo" #endif EOF if [ ! -r ${HDRPATH} ]; then echo "fatal: error creating ${HDRPATH}" exit 1 fi swirc-3.5.5/htmlhelp/000077500000000000000000000000001501213070300144605ustar00rootroot00000000000000swirc-3.5.5/htmlhelp/README.md000066400000000000000000000002231501213070300157340ustar00rootroot00000000000000# Microsoft Compiled HTML Help # This directory contains the source of Swirc's CHM file. It is compiled with a program named HTML Help Workshop. swirc-3.5.5/htmlhelp/about.htm000066400000000000000000000021571501213070300163110ustar00rootroot00000000000000 About

About

Swirc is a BSD licensed, console based and lightweight ICB and IRC client written in C/C++, whose goals are to be portable and secure.

Features

  • TLS/SSL
  • Multiple IRCv3 features such as SASL auth mechanism
  • Themes
  • DCC
  • Identd
  • Nicklist
  • SOCKS 5 proxy
  • Spell checking
  • And more...

Although Swirc is console based it runs natively on Microsoft Windows using the PDCurses library, which isn't common compared to other similar clients.

The name Swirc means Swift IRC (or at your option, Swedish IRC).

System requirements

  • Internet connection
swirc-3.5.5/htmlhelp/build.mk000066400000000000000000000005251501213070300161120ustar00rootroot00000000000000HTMLHELP_DIR = htmlhelp^\ TGTS = $(TGTS) $(HTMLHELP_DIR)swirc.chm CHM_DEPS = $(HTMLHELP_DIR)about.htm\ $(HTMLHELP_DIR)confopts.htm\ $(HTMLHELP_DIR)progopts.htm\ $(HTMLHELP_DIR)style.css\ $(HTMLHELP_DIR)swirc.hhc\ $(HTMLHELP_DIR)swirc.hhk\ $(HTMLHELP_DIR)swirc.hhp $(HTMLHELP_DIR)swirc.chm: $(CHM_DEPS) -hhc $(HTMLHELP_DIR)swirc.hhp swirc-3.5.5/htmlhelp/confopts.htm000066400000000000000000000650341501213070300170350ustar00rootroot00000000000000 Configuration options

Configuration options in swirc.conf

NOTE Certain settings take effect first after program restart while others don't!

account_notify (bool)
Allows a client to be notified when another client’s accountname changes
    
account_tag (bool)
Turns the IRCv3 account tag extension on/off.
    
alt_nick (string)
Alternative nickname. This nickname is used when processing a connection to a server and the default nickname is busy.
    
auto_op_yourself (bool)
Automatically op yourself on channel join when you're identified as an IRC operator
    
awaymsgs_in_privconv (bool)
Output away messages in private conversations? Away messages are often displayed each time you message the user who's marked as being away which can be annoying if the user isn't really away, i.e. he/she just forgot to unset the status. This setting defaults to YES.
    
away_notify (bool)
Allows a client to specify that it would like to be notified when users are marked/unmarked as away
    
beeps (bool)
swirc alerts the user by sending a beep in certain cases. However, with the help of this setting, beeps can be turned on/off.
    
chanserv_host (string)
ChanServ hostname
    
chghost (bool)
Allows servers to send a notification when clients change their username or host (IRCv3 feature). Defaults to yes.
    
cipher_suite (string)
Cipher suite. Which can have one of the following values:
  • secure
  • compat
  • legacy
  • all
cmd_hist_size (int)
Command history size. No more than this number of commands will be stored in the memory.
    
cmd_type_prot (bool)
Command type protection. Detects up to 5 leading spaces followed by a command character preventing a command to be accidentally transmitted as a chat message.
    
connection_timeout (int)
Connection timeout in seconds
    
ctcp_reply (bool)
Reply to CTCP requests? (For example TIME and VERSION.)
    
ctcp_userinfo (string)
A string to return on a CTCP userinfo request.
    
dcc (bool)
Use DCC (Direct Client-to-Client)? Swirc implements its own variant of DCC meaning it's incompatible with other IRC clients. Transport Layer Security is forced and for now the DCC feature isn't available in ICB mode.
    
dcc_cipher_suite (string)
DCC cipher suite. Which can be: secure, compat, legacy or all.
    
dcc_own_ip (string)
IPv4 address for incoming DCC connections. If empty Swirc attempts to resovle the address automatically.
    
dcc_port (int)
Port number for incoming DCC connections (1024-65535), defaults to 8080. Must not be blocked by a firewall.
    
dcc_upload_dir (string)
Where shall Swirc look for DCC uploads? If empty it defaults to uploads in the home dir of Swirc. On OpenBSD this dir is made available with read-only permissions by using unveil(2). However, that is done once at startup. Changing it while Swirc is running will not work.
    
extended_join (bool)
Turns the extended-join IRCv3 feature on/off.
    
ftp_host (string)
FTP hostname.
    
ftp_port (int)
FTP port number (1-65535). Defaults to 21.
    
ftp_user (string)
FTP username. Defaults to anonymous.
    
ftp_pass (string)
FTP password.
    
ftp_upload_dir (string)
FTP upload directory.
    
hostname_checking (bool)
Enable or disable TLS/SSL hostname verification
    
iconv_conversion (bool)
Perform character conversion using GNU libiconv? If having this option set to 'on' causes troubles or if you don't need it, it can safely be switched off. Switching it off boosts the performance of the printtext module significantly.
    
identd (bool)
Use the built-in ident protocol daemon/server? It's automatically started and stopped during the connection process if this option is enabled. During the connection process to an IRC server the IRC server often attempts to send an ident query to port 113 on your computer. You might have seen something like:
	*** Processing connection to irc.server.com
	*** Looking up your hostname...
	*** Checking Ident
	*** No Ident response <---
	*** Found your hostname
(This requires port 113 to be open, i.e. not behind a firewall.)
    
identd_fakenames (bool)
Respond to ident queries with fake (randomized) names?
    
identd_port (int)
Which port shall the ident server listen on? In general, on Unix only root can listen on ports below 1024. Since you neither CAN or SHOULD run Swirc as root you should specify a different port using this setting and configure your firewall to redirect connections to port 113 to this one.
If you're using OpenBSD and pf(4), pose that you want use port 6500 and that the name of the target interface is vio0. You could then add the following lines to your pf.conf(5):
	pass in on vio0 inet  proto tcp from any to any port auth \
	    rdr-to 127.0.0.1 port 6500
	pass in on vio0 inet6 proto tcp from any to any port auth \
	    rdr-to ::1 port 6500
			
    
invite_notify (bool)
Allows a client to specify that it would like to be notified when users are invited to channels
    
joins_parts_quits (bool)
Show JOIN/PART/QUIT events?
    
kick_close_window (bool)
If the active user gets kicked out from a channel, should the channel window be terminated?
    
max_chat_windows (int)
Max chat windows that can be open simultaneously
    
mouse (bool)
Use the mouse?
    
mouse_events (string)
Which mouse events shall be reported?
  • all
  • wheel (default)
    
nickname (string)
Online nickname
    
nickname_aliases (string)
A space separated list of nickname aliases which are used, in addition to the default nickname, to highlight a message if it matches any of the aliases given by this setting.
    
nickserv_host (string)
NickServ hostname
    
notifications (bool)
Turns notifications on/off. Takes effect at once.
    
part_message (string)
Message when leaving a channel
    
qbot_host (string)
Q bot hostname. The Q bot is a QuakeNet service.
    
quit_message (string)
Message when disconnecting from a server
    
real_name (string)
Specifies the real name.
But can be set to anything.
    
reconnect_backoff_delay (int)
The number of seconds that should be added to each reconnect attempt (0-99)
    
reconnect_delay (int)
Seconds to consume before the first reconnect attempt (0-999)
    
reconnect_delay_max (int)
Maximum reconnect delay in seconds (0-999). Regardless of the other related reconnect settings.
    
reconnect_retries (int)
If the IRC connection is lost, how many attempts should be performed to get the connection working again before giving up?
    
sasl (bool)
Request SASL authentication on connection to a server?
    
sasl_mechanism (string)
SASL mechanism. Available mechanisms are:
  • ECDSA-NIST256P-CHALLENGE
  • EXTERNAL
  • PLAIN
  • SCRAM-SHA-1
  • SCRAM-SHA-256
  • SCRAM-SHA-512
Be sure to write them in all uppercase!
    
sasl_password (string)
SASL password. (For mechanism PLAIN and SCRAM-SHA-256.)
It is recommended to set this setting using the interactive sasl command. However, if the initial character is a question mark (‘?’) it symbolizes that the password is in plain text/unencrypted; while a hash mark (‘#’) symbolizes that the password is encrypted. The initial character must be either of them and is not interpreted as a part of the password.
    
sasl_username (string)
SASL username
    
sasl_x509 (string)
Filename for your certificate chain file. The file shall be located in Swirc's home dir and be in PEM format.
The certificate chain file is used for automatic NickServ authentication using the external SASL auth mechanism.
  • 1-root-ca.sh (Create the root CA)
  • 4-client-cert.sh (Create the client certificate and sign it with the root CA)
After running the scripts stated above in given order you can set this setting to client.pem.
    
server_time (bool)
Enable or disable IRCv3 server time extension. The server time extension is particularly useful if you're using an IRC bouncer like ZNC.
    
show_ping_pong (bool)
Show ping pong events?
The default is NO.
    
skip_motd (bool)
Skip message of the day (MOTD) on connection to a server?
    
socks (bool)
Use the SOCKS proxy client? The SOCKS proxy client is particularly suited for use with Tor. Examples of IRC networks that are accessible via Tor are Libera Chat and OFTC. In order to access Libera Chat or OFTC using the previously mentioned technique you can add the following lines to your torrc(5):
# Libera Chat
MapAddress palladium.libera.chat libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd.onion

# OFTC
MapAddress irc.oftc.net oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion
		
After this has been done (and after restarting Tor) you should be able to connect to one of the mapped addresses inside Swirc. The socks host setting should point to the machine where the Tor service is running.
    
socks_atyp (int)
SOCKS address type. Which can have one of the following values:
  • (0) FQDN. (The default.)
  • (1) IPv4 address
  • (2) IPv6 address
    
socks_host (string)
SOCKS hostname.
    
socks_port (string)
SOCKS port. The default is 9050 which is used by Tor.
    
spell (bool)
Use spelling?
    
spell_lang (string)
Spelling language. The default is en_US.
    
spell_syswide (bool)
Where shall Swirc look for spelling dictionaries? (System wide or in the program settings dir.)
    
ssl_verify_peer (bool)
Verify peer? Setting it to NO decreases TLS/SSL security significantly, but is a must on servers with trusted self signed certificates.
    
startup_greeting (bool)
Enable or disable Swirc startup greeting
    
textbuffer_size_absolute (int)
Max number of elements in a text buffer before head gets removed from scroll back history. Each open window is assigned a buffer with this size, so set a sane value!
    
theme (string)
swirc theme.
    
username (string)
User identity. Preferably to be set to the same as the nickname.
    
swirc-3.5.5/htmlhelp/progopts.htm000066400000000000000000000115021501213070300170460ustar00rootroot00000000000000 Program options and keys

Program options

usage: swirc [-46?CPRSXdipv] [-W password] [-c server[:port]] [-j join] [-n nickname] [-r rl name] [-u username] [-x config]

-4 Use IPv4 addresses only
-6 Use IPv6 addresses only
-?, --help Output help
-C Do not change color definitions. If the terminal used to run swirc supports >= 256 colors and can_change_color(3) is true swirc uses init_color(3) to initialize the extended IRC color palette. Which may or may not already be set correctly by the terminal. This option is useful in case the terminal look strange after exit, which is possible to fix by simply restarting it.
-P Permanently disable SASL authentication. If specified, the effect is final (i.e. it overrides any config file value.)
-R Disable TLS/SSL peer verification
-S Force TLS (Transport Layer Security)
-W <password> Equal effect as flag 'p' but non-interactive
-X Disable all IRCv3 extensions
-c <server[:port]> Connect to given server. If the port is omitted port 6667 will be chosen. And if the port is 7326 ICB mode is turned on automatically. Further, if the port is 6697 swirc attempts to initiate a TLS/SSL connection.
-d Debug logging
-i Turn on Internet Citizen's Band mode
-j <join> A comma-separated list of channels to join
-n <nickname> Online nickname
-p Server password (for private servers). However: InspIRCd has a module called password forward, which means that if a server password is specified by this flag, it will be used to identify to NickServ. If so: connect with a TLS/SSL connection, i.e. an encrypted connection, to prevent your password from being disclosed in clear text. swirc also supports IRCv3 SASL authentication which is probably a better alternative.
-r <rl name> Your real name
-u <username> Your username
-v, --version Output swirc version
-x <config> Config file

Keys

CTRL+aMove to beginning of line
CTRL+eMove to end of line
CTRL+bMove cursor backward
CTRL+fMove cursor forward
CTRL+dDelete
CTRL+gClear readline input
CTRL+nNext window
CTRL+pPrevious window
PG UPScroll up
PG DOWNScroll down
Up arrowHistory previous
Down arrowHistory next
F2Spell word
F3Scroll nicklist up
F4Scroll nicklist down
F11Close window
F12Close all private conversations

Inserting text-decoration

F5Blink
F6Bold
F7Color
F8Normal
F9Reverse
F10Underline
swirc-3.5.5/htmlhelp/style.css000066400000000000000000000005151501213070300163330ustar00rootroot00000000000000body { background-color: white; color: black; font-family: sans-serif; } h1, h2, h3, h4, h5, h6 { color: royalblue; font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; } a { color: darkgoldenrod; text-decoration: none; } a:hover { color: darkgoldenrod; text-decoration: underline; } swirc-3.5.5/htmlhelp/swirc.hhc000066400000000000000000000015031501213070300162720ustar00rootroot00000000000000
swirc-3.5.5/htmlhelp/swirc.hhk000066400000000000000000000003021501213070300162760ustar00rootroot00000000000000
swirc-3.5.5/htmlhelp/swirc.hhp000066400000000000000000000005121501213070300163060ustar00rootroot00000000000000[OPTIONS] Binary Index=No Compatibility=1.1 or later Compiled file=swirc.chm Contents file=swirc.hhc Default Font=Comic Sans MS,8,0 Default topic=about.htm Display compile progress=Yes Index file=swirc.hhk Language=0x409 Engelska (USA) Title=Swirc IRC client [FILES] about.htm confopts.htm progopts.htm themes.htm [INFOTYPES] swirc-3.5.5/htmlhelp/themes.htm000066400000000000000000000333231501213070300164630ustar00rootroot00000000000000 Writing themes

Writing themes

This manual page is the ultimate reference to consult when writing themes for Swirc.

TEXT DECORATION

What Hex Octal Appearance
Blink 1d 035 ^]
Bold 02 002 ^B
Color 03 003 ^C
Normal 0f 017 ^O
Reverse 16 026 ^V
Underline 1f 037 ^_

Example usage:

	blabla ^Bbold text^B blabla
	blabla ^Vreversed text^V blabla
	blabla ^_underlined text^_ blabla

Always use the literal control characters because else it won't work!

Blink is a no operation because it's annoying.

COLORS

Number Name  
00 white  
01 black  
02 blue (navy)
03 green  
04 red  
05 brown (maroon)
06 purple  
07 orange (olive)
08 yellow  
09 lt.green (lime)
10 teal (a kinda green/blue cyan)
11 lt.cyan (cyan ?) (aqua)
12 lt.blue (royal)
13 pink (light purple) (fuchsia)
14 grey  
15 lt.grey (silver)

For the colors 16-99 see the output of the command /colormap. Enough color pairs must've been initialized. (193 color pairs are too few for the colors 16-99 to be used as backgrounds.)

The syntax of the color attribute in text has the format ^CN[,M]. N will be the text (foreground) color and M the background color. A background color (M) is optional and is not always included.

N and M can maximally be two digits long. Although the colors {0,1,2,...,9} are supported you are highly encouraged to use {00,01,02,...,09}.

A plain ^C can be used to turn the color effect off. While typing ^O will make sure ALL the text effects gets reset. For example:

	blabla ^C05,02red text on blue background^C blabla
	blabla ^C09green text^O blabla

Of course settings can start with colored text and a closing ^C is not essential.

SETTINGS

term_background (int)
Which background color (0-15) is this theme written for?
	0 = white
	1 = black
    
term_enable_colors (bool)
Enable colors (yes/no)?
    
term_use_default_colors (bool)
Use terminal's default colors? I.e. call use_default_colors(3).
    
color3, color4 (string)
Colors used in uncategorized contexts. Must begin with ^C.
    
gfx_failure, gfx_success, gfx_warning (string)
Used in contexts where to flag failures, successes and warnings respectively.
    
left_bracket, right_bracket (string)
Left and right bracket. Frequently used.
    
logo_color (string)
Swirc ASCII logo color displayed at startup. (Must begin with ^C.)
    
nick_s1, nick_s2 (string)
When you or another user types something the nickname will be enclosed by s1 and s2.
    
nicklist_my_nick_color, nicklist_nick_color, nicklist_privilege_color, nicklist_vline_color (int)
Nicklist decoration. All of these settings are of type int (0-99) thus ^C is not needed and should not be used.
    
notice_color1, notice_color2 (string)
Notice colors. ^C should be used.
    
notice_lb, notice_rb, notice_sep (string)
Notice left/right bracket and separator.
    
notice_inner_b1, notice_inner_b2 (string)
Notice inner bracket 1 and 2. (I.e. left and right.)
    
primary_color, secondary_color (string)
Primary and secondary color for the theme. Frequently used. (Begin with ^C.)
    
slogan (string)
Swirc slogan displayed in the statusbar.
    
specifier1, specifier2, specifier3 (string)
Specifiers used in various contexts. Number 1 is frequently used.
    
statusbar_bg, statusbar_fg (string)
Statusbar background and foreground. Valid values are black, red, green, yellow, blue, magenta, cyan, white.
    
statusbar_leftBracket, statusbar_rightBracket, statusbar_spec (string)
Statusbar left/right bracket and specifier.
    
time_format (string)
Time format passed to strftime(3).
    
titlebar_bg, titlebar_fg (string)
Titlebar background and foreground. Valid values are black, red, green, yellow, blue, magenta, cyan, white.
    
whois_acc (string)
    
whois_away (string)
    
whois_bot (string)
    
whois_cert (string)
    
whois_channels (string)
    
whois_conn (string)
    
whois_host (string)
    
whois_idle (string)
    
whois_ircName (string)
    
whois_ircOp (string)
    
whois_modes (string)
    
whois_server (string)
    
whois_service (string)
    
whois_ssl (string)
    

CAVEATS

If you want to give color to numbers be sure to use two digits for N nor M!

swirc-3.5.5/maketargets/000077500000000000000000000000001501213070300151525ustar00rootroot00000000000000swirc-3.5.5/maketargets/install.mk000066400000000000000000000032631501213070300171550ustar00rootroot00000000000000INSTALL = install INSTALL_DEPS = src/swirc.1\ swirc\ swirc-royal.png\ swirc.conf.5\ swirc.theme.5 LC_MSGS = po/de/swirc.mo\ po/fi/swirc.mo\ po/fr/swirc.mo\ po/sv/swirc.mo # Don't provide a default value for DESTDIR. It should be empty. DESTDIR ?= DEST_PROGRAM = $(DESTDIR)$(PREFIX)/bin DEST_MANUAL = $(DESTDIR)$(PREFIX)/share/man/man1 DEST_CONFMAN = $(DESTDIR)$(PREFIX)/share/man/man5 DEST_LOGO = $(DESTDIR)$(PREFIX)/share/swirc DEST_LC_MSGS = $(DESTDIR)$(PREFIX)/share/locale/ install: $(INSTALL_DEPS) $(LC_MSGS) $(INSTALL) -d $(DEST_PROGRAM) $(INSTALL) -d $(DEST_MANUAL) $(INSTALL) -d $(DEST_CONFMAN) $(INSTALL) -d $(DEST_LOGO) $(INSTALL) -d $(DEST_LC_MSGS)de/LC_MESSAGES $(INSTALL) -d $(DEST_LC_MSGS)fi/LC_MESSAGES $(INSTALL) -d $(DEST_LC_MSGS)fr/LC_MESSAGES $(INSTALL) -d $(DEST_LC_MSGS)sv/LC_MESSAGES $(INSTALL) -m 0755 swirc $(DEST_PROGRAM) $(INSTALL) -m 0444 src/swirc.1 $(DEST_MANUAL) $(INSTALL) -m 0444 swirc.conf.5 $(DEST_CONFMAN) $(INSTALL) -m 0444 swirc.theme.5 $(DEST_CONFMAN) $(INSTALL) -m 0444 swirc-royal.png $(DEST_LOGO) $(INSTALL) -m 0644 po/de/swirc.mo $(DEST_LC_MSGS)de/LC_MESSAGES $(INSTALL) -m 0644 po/fi/swirc.mo $(DEST_LC_MSGS)fi/LC_MESSAGES $(INSTALL) -m 0644 po/fr/swirc.mo $(DEST_LC_MSGS)fr/LC_MESSAGES $(INSTALL) -m 0644 po/sv/swirc.mo $(DEST_LC_MSGS)sv/LC_MESSAGES install-no-lc-msgs: $(INSTALL_DEPS) $(INSTALL) -d $(DEST_PROGRAM) $(INSTALL) -d $(DEST_MANUAL) $(INSTALL) -d $(DEST_CONFMAN) $(INSTALL) -d $(DEST_LOGO) $(INSTALL) -m 0755 swirc $(DEST_PROGRAM) $(INSTALL) -m 0444 src/swirc.1 $(DEST_MANUAL) $(INSTALL) -m 0444 swirc.conf.5 $(DEST_CONFMAN) $(INSTALL) -m 0444 swirc.theme.5 $(DEST_CONFMAN) $(INSTALL) -m 0444 swirc-royal.png $(DEST_LOGO) swirc-3.5.5/maketargets/tidy.mk000066400000000000000000000006001501213070300164500ustar00rootroot00000000000000# The 'tidy' target TIDY = clang-tidy TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet SW_CLANG_TIDYFLAGS ?= tidy: $(SRC_DIR)include/swircpaths.h $(TIDY) $(COMMANDS_SRCS) $(EVENTS_SRCS) $(SRCS) $(TIDYFLAGS) \ $(SW_CLANG_TIDYFLAGS) -- $(CPPFLAGS) swirc-3.5.5/options.w32.mk000066400000000000000000000031031501213070300152760ustar00rootroot00000000000000CC = cl CFLAGS = -MT -O2 -W3 -experimental:c11atomics -nologo -std:c17 CXX = $(CC) CXXFLAGS = -DUNICODE=1 -EHa -MT -O2 -W3 -Zc:__cplusplus -nologo -std:c++17 # C preprocessor flags CPPFLAGS = -DHAVE_ATLSTR_H=1\ -DHAVE_HUNSPELL=1\ -DHAVE_LIBICONV=1\ -DHAVE_LIBIDN=1\ -DHAVE_LIBINTL_H=1\ -DHAVE_LIBINTL_SETLOCALE=1\ -DHAVE_STRCASESTR=0\ -DNDEBUG=1\ -DPDC_EXP_EXTRAS=1\ -DPDC_NCMOUSE=1\ -DTOAST_NOTIFICATIONS=1\ -DWIN32=1\ -DWIN32_LEAN_AND_MEAN=1 # Versions CURL_VERSION = 8.8.0 HUNSPELL_VERSION = 1.7.2 LIBRESSL_VERSION = 4.1.0 PDCURSES_VERSION = 3.9 GNU_BUNDLE_DATE = 202205 LOCALES_SNAP = 20250517 # E and Q E = @echo Q = @ MACHINE = x64 NAME_libcharset = libcharset-1 NAME_libcrypto = crypto NAME_libhunspell = libhunspell-1.7-0 NAME_libiconv = libiconv-2 NAME_libidn = libidn-12 NAME_libintl = libintl-8 NAME_libssl = ssl # LibreSSL DLLs DLL_crypto = crypto-56.dll DLL_ssl = ssl-59.dll # MINGW hunspell DLL_libgcc = libgcc_s_seh-1.dll DLL_libstdcpp = libstdc++-6.dll DLL_libwinpthread = libwinpthread-1.dll LDFLAGS = -LIBPATH:curl-$(CURL_VERSION)/$(MACHINE)\ -LIBPATH:gnu-bundle-$(GNU_BUNDLE_DATE)/$(MACHINE)\ -LIBPATH:hunspell-$(HUNSPELL_VERSION)/$(MACHINE)\ -LIBPATH:libressl-$(LIBRESSL_VERSION)/$(MACHINE)\ -LIBPATH:pdcurses-$(PDCURSES_VERSION)/$(MACHINE) LDLIBS = $(NAME_libcharset).lib\ $(NAME_libcrypto).lib\ $(NAME_libhunspell).lib\ $(NAME_libiconv).lib\ $(NAME_libidn).lib\ $(NAME_libintl).lib\ $(NAME_libssl).lib\ advapi32.lib\ bcrypt.lib\ kernel32.lib\ libcurl.lib\ pdcurses.lib\ runtimeobject.lib\ user32.lib\ ws2_32.lib RM = @del /q SLASH_SYM = ^\ swirc-3.5.5/po/000077500000000000000000000000001501213070300132615ustar00rootroot00000000000000swirc-3.5.5/po/Makefile000066400000000000000000000007041501213070300147220ustar00rootroot00000000000000ROOT = ../ COMMANDS_DIR := $(ROOT)src/commands/ EVENTS_DIR := $(ROOT)src/events/ SRC_DIR := $(ROOT)src/ include $(ROOT)options.mk include common.mk all: main include targets/merge.mk main: $(PKG).pot $(POFILES) $(MOFILES) $(PKG).pot: $(Q) $(XGETTEXT) $(XGTFLAGS) $(INPUTFILES) include targets/lang/de.mk include targets/lang/fi.mk include targets/lang/fr.mk include targets/lang/sv.mk clean: $(E) " CLEAN" $(RM) $(PKG).pot $(RM) $(MOFILES) swirc-3.5.5/po/Makefile.vc000066400000000000000000000007471501213070300153400ustar00rootroot00000000000000# -*- mode: makefile; -*- ROOT = ../ COMMANDS_DIR = $(ROOT)src/commands/ EVENTS_DIR = $(ROOT)src/events/ SRC_DIR = $(ROOT)src/ !include $(ROOT)options.w32.mk !include common.mk all: main !include targets/merge.mk main: $(PKG).pot $(POFILES) $(MOFILES) $(PKG).pot: $(Q) $(XGETTEXT) $(XGTFLAGS) $(INPUTFILES) !include targets/lang/de.mk !include targets/lang/fi.mk !include targets/lang/fr.mk !include targets/lang/sv.mk clean: $(E) ^ ^ CLEAN $(RM) $(PKG).pot $(RM) $(MOFILES) swirc-3.5.5/po/common.mk000066400000000000000000000022701501213070300151030ustar00rootroot00000000000000# Common make defs PKG = swirc MSGMERGE = msgmerge MMFLAGS = --backup=none\ --sort-by-file\ --update XGETTEXT = xgettext XGTFLAGS = --add-comments\ --c++\ --copyright-holder="Markus Uhlin"\ --default-domain=$(PKG)\ --foreign-user\ --keyword=N_\ --keyword=_\ --msgid-bugs-address="https://github.com/uhlin/swirc/issues"\ --output=$(PKG).pot\ --sort-by-file INPUTFILES = $(COMMANDS_DIR)colormap.cpp\ $(COMMANDS_DIR)connect.c\ $(COMMANDS_DIR)ctcp.c\ $(COMMANDS_DIR)dcc.cpp\ $(COMMANDS_DIR)ftp.cpp\ $(COMMANDS_DIR)theme.c\ $(EVENTS_DIR)account.cpp\ $(EVENTS_DIR)away.cpp\ $(EVENTS_DIR)channel.cpp\ $(EVENTS_DIR)chghost.cpp\ $(EVENTS_DIR)invite.cpp\ $(EVENTS_DIR)misc.cpp\ $(SRC_DIR)include/commandhelp.h\ $(SRC_DIR)interpreter.cpp\ $(SRC_DIR)io-loop.c\ $(SRC_DIR)irc.c\ $(SRC_DIR)log.c\ $(SRC_DIR)main.cpp\ $(SRC_DIR)messagetags.c\ $(SRC_DIR)nestHome.c\ $(SRC_DIR)network.cpp\ $(SRC_DIR)readline.c\ $(SRC_DIR)sig-w32.c\ $(SRC_DIR)spell.cpp\ $(SRC_DIR)statusbar.cpp\ $(SRC_DIR)tls-server.cpp POFILES = de/$(PKG).po\ fi/$(PKG).po\ fr/$(PKG).po\ sv/$(PKG).po MOFILES = de$(SLASH_SYM)$(PKG).mo\ fi$(SLASH_SYM)$(PKG).mo\ fr$(SLASH_SYM)$(PKG).mo\ sv$(SLASH_SYM)$(PKG).mo swirc-3.5.5/po/de/000077500000000000000000000000001501213070300136515ustar00rootroot00000000000000swirc-3.5.5/po/de/swirc.po000066400000000000000000001360341501213070300153470ustar00rootroot00000000000000# German translations for swirc package # German translation for swirc. # This file is put in the public domain. # , 2021. # msgid "" msgstr "" "Project-Id-Version: swirc 3.5.5\n" "Report-Msgid-Bugs-To: https://github.com/uhlin/swirc/issues\n" "POT-Creation-Date: 2025-05-17 12:31+0200\n" "PO-Revision-Date: 2025-05-17 12:27+0200\n" "Last-Translator: Markus Uhlin \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/commands/colormap.cpp:153 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:222 msgid "Yes" msgstr "Ja" #: ../src/commands/colormap.cpp:154 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:223 msgid "No" msgstr "Nein" #: ../src/commands/connect.c:211 msgid "Connect using password? [Y/n]: " msgstr "Mit Passwort verbinden? [Y/n]: " #: ../src/commands/connect.c:219 ../src/commands/connect.c:280 #: ../src/commands/connect.c:327 ../src/nestHome.c:202 msgid "input too big" msgstr "Benutzereingabe zu groß" #: ../src/commands/connect.c:248 #, c-format msgid "%s: string copy error" msgstr "%s: String Kopierfehler" #: ../src/commands/connect.c:264 msgid "Server password (will not echo): " msgstr "Server Passwort (wird nicht widerhallen): " #: ../src/commands/connect.c:317 #, c-format msgid "Your choice (0-%d): " msgstr "Ihre Wahl (0-%d): " #: ../src/commands/connect.c:520 #, c-format msgid "Next reconnect attempt in %ld seconds..." msgstr "Nächster Wiederverbindungsversuch in %ld Sekunden..." #: ../src/commands/connect.c:532 msgid "Reconnection aborted!" msgstr "Wiederverbindung abgebrochen!" #: ../src/commands/connect.c:572 msgid "Cannot initiate use of the Winsock DLL" msgstr "Verwendung der Winsock DLL kann nicht initiiert werden" #: ../src/commands/connect.c:576 msgid "Use of the Winsock DLL granted" msgstr "Nutzung der Winsock DLL gewährt" #: ../src/commands/ctcp.c:77 ../src/commands/ftp.cpp:873 #: ../src/commands/ftp.cpp:960 ../src/commands/ftp.cpp:1009 #: ../src/commands/ftp.cpp:1046 ../src/commands/ftp.cpp:1128 msgid "Insufficient arguments" msgstr "Unzureichende Argumente" #: ../src/commands/ctcp.c:86 #, c-format msgid "%s: insufficient arguments" msgstr "%s: nicht genügend Argumente" #: ../src/commands/ctcp.c:89 #, c-format msgid "%s: invalid target" msgstr "%s: ungültiges Target" #: ../src/commands/ctcp.c:94 #, c-format msgid "%s: forbidden channel name characters" msgstr "%s: verbotene Zeichen im Kanalnamen" #: ../src/commands/ctcp.c:105 msgid "Invalid CTCP query" msgstr "Ungültige CTCP Abfrage" #: ../src/commands/dcc.cpp:212 msgid "Write error" msgstr "Dateischreibfehler" #: ../src/commands/dcc.cpp:337 #, c-format msgid "%s: wrote: %s" msgstr "%s: schrieb: %s" #: ../src/commands/dcc.cpp:340 #, c-format msgid "%s: did not complete: %s" msgstr "%s: wurde nicht abgeschlossen: %s" #: ../src/commands/dcc.cpp:1017 #, c-format msgid "%sStarted%s: %s" msgstr "%sGestartet%s: %s" #: ../src/commands/dcc.cpp:1019 #, c-format msgid "%sStopped%s: %s" msgstr "%sGestoppt%s: %s" #: ../src/commands/dcc.cpp:1393 #, c-format msgid "sending '%s' to %s (%.1f%c)..." msgstr "sende '%s' an %s (%.1f%c)..." #: ../src/commands/dcc.cpp:1484 msgid "Missing certs. Not starting the DCC server. Please create them." msgstr "" "Fehlende Zertifikate. DCC Server wird nicht gestartet. Bitte erstellen Sie " "sie." #: ../src/commands/dcc.cpp:1590 #, c-format msgid "%s: added: '%s' (%.1f%c)" msgstr "%s: hinzugefügt: '%s' (%.1f%c)" #: ../src/commands/dcc.cpp:1592 #, c-format msgid "%s: from: %s <%s@%s>" msgstr "%s: von: %s <%s@%s>" #: ../src/commands/dcc.cpp:1594 msgid "To get the file, type:" msgstr "Um die Datei abzurufen, geben Sie Folgendes ein:" #: ../src/commands/dcc.cpp:1845 ../src/commands/ftp.cpp:805 #, c-format msgid "%s: file read error" msgstr "%s: Fehler beim Lesen der Datei" #: ../src/commands/dcc.cpp:1850 #, c-format msgid "%s: tls write error" msgstr "" #: ../src/commands/dcc.cpp:1867 #, c-format msgid "started: %s" msgstr "gestartet: %s" #: ../src/commands/dcc.cpp:1868 #, c-format msgid "stopped: %s" msgstr "gestoppt: %s" #: ../src/commands/dcc.cpp:1885 msgid "read request error" msgstr "Leseanforderungsfehler" #: ../src/commands/dcc.cpp:1892 msgid "unable to find the send object" msgstr "Das Sendeobjekt konnte nicht gefunden werden" #: ../src/commands/dcc.cpp:1900 msgid "already sent file" msgstr "bereits gesendete Datei" #: ../src/commands/dcc.cpp:1903 msgid "the send object is in a locked state" msgstr "das Sendeobjekt befindet sich im gesperrten Zustand" #: ../src/commands/dcc.cpp:1908 msgid "file open error" msgstr "Fehler beim Öffnen der Datei" #: ../src/commands/dcc.cpp:1927 #, c-format msgid "%s: successfully sent file: %s" msgstr "%s: Datei erfolgreich gesendet: %s" #: ../src/commands/dcc.cpp:1931 #, c-format msgid "%s: file transfer incomplete: %s" msgstr "%s: Dateiübertragung unvollständig: %s" #: ../src/commands/ftp.cpp:181 msgid "Invalid username" msgstr "Ungültiger Benutzername" #: ../src/commands/ftp.cpp:183 msgid "Too long password" msgstr "Zu langes Passwort" #: ../src/commands/ftp.cpp:185 msgid "Invalid hostname" msgstr "Ungültiger Hostname" #: ../src/commands/ftp.cpp:187 msgid "Invalid port number" msgstr "Ungültige Portnummer" #: ../src/commands/ftp.cpp:190 ../src/network.cpp:308 msgid "Unable to get a list of IP addresses" msgstr "Es kann keine Liste mit IP-Adressen abgerufen werden" #: ../src/commands/ftp.cpp:193 ../src/commands/ftp.cpp:533 #: ../src/network.cpp:175 msgid "Failed to establish a connection" msgstr "Fehler beim Herstellen einer Verbindung" #: ../src/commands/ftp.cpp:204 ../src/commands/ftp.cpp:886 #: ../src/commands/ftp.cpp:1257 msgid "Cannot send" msgstr "Senden nicht möglich" #: ../src/commands/ftp.cpp:521 msgid "Failed to create an endpoint for communication" msgstr "Es konnte kein Endpunkt für die Kommunikation erstellt werden" #: ../src/commands/ftp.cpp:813 #, c-format msgid "%s: bytes sent mismatch bytes read" msgstr "%s: gesendete Bytes stimmen nicht mit gelesenen Bytes überein" #: ../src/commands/ftp.cpp:876 ../src/commands/ftp.cpp:1245 msgid "No control connection" msgstr "" #: ../src/commands/ftp.cpp:879 ../src/commands/ftp.cpp:901 #: ../src/commands/ftp.cpp:1253 msgid "Invalid network socket" msgstr "Ungültiger Netzwerk Socket" #: ../src/commands/ftp.cpp:963 ../src/commands/ftp.cpp:1049 msgid "Blank file path" msgstr "Leerer Dateipfad" #: ../src/commands/ftp.cpp:1249 msgid "Already in passive mode" msgstr "Bereits im passiven Modus" #: ../src/commands/ftp.cpp:1272 msgid "Failed to enter passive mode" msgstr "Der Wechsel in den passiven Modus ist fehlgeschlagen" #: ../src/commands/theme.c:313 #, c-format msgid "failed to remove: %s" msgstr "Entfernen fehlgeschlagen: %s" #: ../src/commands/theme.c:333 msgid "no such theme in the database" msgstr "" #: ../src/commands/theme.c:353 msgid "theme installed (use 'set' to activate it)" msgstr "" #: ../src/commands/theme.c:356 msgid "failed to install the theme :-(" msgstr "" #: ../src/commands/theme.c:404 msgid "non-existent" msgstr "nicht existent" #: ../src/commands/theme.c:417 msgid "theme activated" msgstr "Thema aktiviert" #: ../src/events/account.cpp:56 #, c-format msgid "%s%s%c %s%s@%s%s has logged into a new account %s%s%c" msgstr "%s%s%c %s%s@%s%s hat sich bei einem neuen Konto %s%s%c angemeldet" #: ../src/events/account.cpp:69 #, c-format msgid "%s%s%c %s%s@%s%s has logged out of their account" msgstr "%s%s%c %s%s@%s%s hat sich von seinem Konto abgemeldet" #: ../src/events/away.cpp:55 #, c-format msgid "%s%s%c %s%s@%s%s is no longer marked as being away!" msgstr "" #: ../src/events/away.cpp:68 #, c-format msgid "%s%s%c %s%s@%s%s has been marked as being away (%s)" msgstr "%s%s%c %s%s@%s%s wurde als abwesend markiert (%s)" #: ../src/events/channel.cpp:100 #, c-format msgid "Homepage for %s%s%s%c%s: %s" msgstr "Startseite für %s%s%s%c%s: %s" #: ../src/events/channel.cpp:223 #, c-format msgid "%s%s%c %s%s@%s%s has joined %s%s%c" msgstr "%s%s%c %s%s@%s%s ist %s%s%c beigetreten" #: ../src/events/channel.cpp:308 #, c-format msgid "%s was kicked from %s%s%c by %s%s%c %s%s%s" msgstr "%s wurde aus %s%s%c geschmissen / %s%s%c %s%s%s" #. #. * User mode #. #: ../src/events/channel.cpp:580 #, c-format msgid "Mode change %s%s%s for user %c%s%c" msgstr "Modusänderung %s%s%s für Benutzer %c%s%c" #: ../src/events/channel.cpp:588 #, c-format msgid "mode/%s%s%s%c%s %s%s%s by %s%s%c" msgstr "mode/%s%s%s%c%s %s%s%s von %s%s%c" #: ../src/events/channel.cpp:703 #, c-format msgid "%s%s%c is now known as %s %s%s%c" msgstr "%s%s%c heißt jetzt %s %s%s%c" #: ../src/events/channel.cpp:785 #, c-format msgid "%s%s%c %s%s@%s%s has left %s%s%c %s%s%s" msgstr "%s%s%c %s%s@%s%s hat %s%s%c verlassen %s%s%s" #: ../src/events/channel.cpp:916 #, c-format msgid "%s%s%c %s%s@%s%s has quit %s%s%s" msgstr "%s%s%c %s%s@%s%s hat sich abgemeldet %s%s%s" #: ../src/events/channel.cpp:1011 #, c-format msgid "Topic for %s%s%s%c%s: %s" msgstr "Thema für %s%s%s%c%s: %s" #: ../src/events/channel.cpp:1071 #, c-format msgid "%c%s%c changed the topic of %c%s%c to: %s" msgstr "%c%s%c hat das Thema von %c%s%c geändert: %s" #: ../src/events/channel.cpp:1149 #, c-format msgid "Topic set by %c%s%c %s%s@%s%s %s%s%s" msgstr "Das Thema wurde von %c%s%c %s%s@%s%s festgelegt %s%s%s" #: ../src/events/channel.cpp:1155 #, c-format msgid "Topic set by %c%s%c %s%s%s" msgstr "Das Thema wurde von %c%s%c festgelegt %s%s%s" #: ../src/events/chghost.cpp:61 #, c-format msgid "%s%s%s has changed their hostname to %s%s@%s%s" msgstr "%s%s%s hat seinen Hostnamen in %s%s@%s%s geändert" #: ../src/events/invite.cpp:56 msgid "The invitation has been passed onto the end user" msgstr "" #: ../src/events/invite.cpp:103 #, c-format msgid "%c%s%c %s%s@%s%s invites you to %c%s%c" msgstr "%c%s%c %s%s@%s%s lädt Sie zu %c%s%c ein" #. #. * TODO: Write to the channels where the user #. * that's doing the invite is in #. #: ../src/events/invite.cpp:113 #, c-format msgid "%c%s%c %s%s@%s%s invites %c%s%c to %c%s%c" msgstr "%c%s%c %s%s@%s%s lädt %c%s%c zu %c%s%c ein" #: ../src/events/misc.cpp:246 #, c-format msgid "Channel %s%s%s%c%s created %s" msgstr "Der Kanal %s%s%s%c%s wurde am %s erstellt" #: ../src/events/misc.cpp:303 #, c-format msgid "mode/%s%s%s%c%s %s%s%s" msgstr "" #: ../src/events/misc.cpp:354 #, c-format msgid "Channel forwarding from %c%s%c to %c%s%c" msgstr "Kanalweiterleitung von %c%s%c zu %c%s%c" #: ../src/events/misc.cpp:431 #, c-format msgid "Nickname %c%s%c is already in use" msgstr "Der Spitzname %c%s%c ist bereits in Gebrauch" #: ../src/events/misc.cpp:439 msgid "Alternative nickname already tested. Disconnecting..." msgstr "Alternativer Spitzname bereits getestet. Verbindung wird getrennt..." #: ../src/events/misc.cpp:444 #, c-format msgid "Attempting to use alt_nick (%s) instead..." msgstr "Versuch, stattdessen alt_nick (%s) zu verwenden..." #: ../src/events/misc.cpp:452 msgid "Disconnecting..." msgstr "Verbindung wird getrennt..." #: ../src/events/misc.cpp:513 msgid "You're now an IRC operator!" msgstr "Sie sind jetzt ein IRC Operator!" #: ../src/include/commandhelp.h:44 msgid "usage: /admin [target]" msgstr "" #: ../src/include/commandhelp.h:46 msgid "" "The admin command is used to find information about the administrator\n" "of the given server, or current server if 'target' parameter is omit-\n" "ted." msgstr "" #: ../src/include/commandhelp.h:53 ../src/include/commandhelp.h:201 #: ../src/include/commandhelp.h:282 msgid "usage:" msgstr "" #: ../src/include/commandhelp.h:60 msgid "Send announcements on IRC." msgstr "" #: ../src/include/commandhelp.h:65 msgid "usage: /away [reason]" msgstr "" #: ../src/include/commandhelp.h:67 msgid "" "Marks yourself as being away (with a reason). If the reason is omitted\n" "you will be marked as no longer being away." msgstr "" #: ../src/include/commandhelp.h:73 msgid "usage: /ban " msgstr "" #: ../src/include/commandhelp.h:75 msgid "" "Sets a channel ban which rejects all users whose 'nick!user@host'\n" "matches the provided mask from joining the channel. Wildcards are okay\n" "and the active window must be an IRC channel." msgstr "" #: ../src/include/commandhelp.h:82 msgid "usage: /banlist [channel]" msgstr "" #: ../src/include/commandhelp.h:84 msgid "" "Outputs a channel's banlist. If the channel argument is left empty and\n" "the active window is an IRC channel, Swirc will output the banlist for\n" "that channel." msgstr "" #: ../src/include/commandhelp.h:91 msgid "usage: /beep " msgstr "" #: ../src/include/commandhelp.h:93 msgid "Send beeps. (ICB only)" msgstr "" #: ../src/include/commandhelp.h:98 msgid "usage: /boot " msgstr "" #: ../src/include/commandhelp.h:100 msgid "Kick a user out of your group. (ICB only)" msgstr "" #: ../src/include/commandhelp.h:105 msgid "usage: /chanserv <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:107 msgid "" "Communicate with your IRC network's channel service. If the initial\n" "argument equals to '--', then the value of setting 'chanserv_host' is\n" "used as a service hostname." msgstr "" #: ../src/include/commandhelp.h:118 msgid "usage: /ctcp " msgstr "" #: ../src/include/commandhelp.h:120 msgid "" "Send a CTCP query to 'target', which is either a nickname or an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:123 msgid "Query can be either:" msgstr "" #: ../src/include/commandhelp.h:131 msgid "usage: /cap [ls | list]" msgstr "" #: ../src/include/commandhelp.h:133 msgid "" "Lists the (IRCv3) capabilities supported by the server and/or the\n" "capabilities associated with the active connection." msgstr "" #: ../src/include/commandhelp.h:139 msgid "usage: /cleartoasts" msgstr "" #: ../src/include/commandhelp.h:141 msgid "" "On Windows Swirc sends toast notifications. By running this command\n" "all notifications associated with Swirc will be cleared." msgstr "" #: ../src/include/commandhelp.h:147 msgid "usage: /close" msgstr "" #: ../src/include/commandhelp.h:149 msgid "" "Closes the active window. It's not possible to close the status\n" "window. And while connected it's not possible to close a channel, in\n" "that case instead use '/part'." msgstr "" #: ../src/include/commandhelp.h:156 msgid "usage: /colormap" msgstr "" #: ../src/include/commandhelp.h:158 msgid "Outputs information about colors." msgstr "" #: ../src/include/commandhelp.h:163 msgid "usage: /connect [-tls] " msgstr "" #: ../src/include/commandhelp.h:165 msgid "Connect to given server." msgstr "" #: ../src/include/commandhelp.h:167 msgid "" "If the port is omitted port 6667 will be chosen. And if the port is\n" "7326 ICB mode is turned on automatically. Further, if the port is 6697\n" "Swirc attempts to initiate a TLS/SSL connection, as well as if '-tls'\n" "is entered." msgstr "" #: ../src/include/commandhelp.h:172 msgid "" "It is possible to connect to a certain IRC network by only entering\n" "the network name. For example: '/connect -tls libera', will connect to\n" "Libera Chat using an encrypted connection. Preprogrammed network names\n" "are:" msgstr "" #: ../src/include/commandhelp.h:192 msgid "usage: /cycle [channel]" msgstr "" #: ../src/include/commandhelp.h:194 msgid "" "Cycle a channel, i.e. '/part' plus '/join'. If the channel argument is\n" "omitted and the active window is an IRC channel, the client will cycle\n" "that channel." msgstr "" #: ../src/include/commandhelp.h:209 msgid "" "Get and send files. Swirc implements its own variant of DCC meaning\n" "it's incompatible with other IRC clients. Transport Layer Security is\n" "forced and for now the DCC feature isn't available in ICB mode." msgstr "" #: ../src/include/commandhelp.h:216 msgid "usage: /deop " msgstr "" #: ../src/include/commandhelp.h:218 msgid "Remove the channel operator privilege from another user." msgstr "" #: ../src/include/commandhelp.h:223 msgid "usage: /devoice " msgstr "" #: ../src/include/commandhelp.h:225 msgid "Remove the channel voice privilege from another user." msgstr "" #: ../src/include/commandhelp.h:230 msgid "usage: /die [--I-am-sure]" msgstr "" #: ../src/include/commandhelp.h:232 msgid "" "An IRC operator can use this command to shutdown the server. Please\n" "confirm that this is what you really want by typing '--I-am-sure'." msgstr "" #: ../src/include/commandhelp.h:238 msgid "usage: /disconnect [message]" msgstr "" #: ../src/include/commandhelp.h:240 msgid "" "Disconnect from IRC, but don't quit the program. A disconnect message\n" "is optional." msgstr "" #: ../src/include/commandhelp.h:246 msgid "usage: /echo " msgstr "" #: ../src/include/commandhelp.h:248 msgid "Writes text to the current window without sending anything." msgstr "" #: ../src/include/commandhelp.h:252 msgid "Echo 'Hello World':" msgstr "" #: ../src/include/commandhelp.h:253 msgid " /echo Hello World" msgstr "" #: ../src/include/commandhelp.h:258 msgid "usage: /exlist [channel]" msgstr "" #: ../src/include/commandhelp.h:260 msgid "" "Outputs a channel's exception list. An exception mask (+e) overrides a\n" "ban mask. If the channel argument is omitted and the active window is\n" "an IRC channel, the client will output the exception list for that\n" "channel." msgstr "" #: ../src/include/commandhelp.h:268 msgid "usage: /fetchdic [name]" msgstr "" #: ../src/include/commandhelp.h:270 msgid "" "Fetches spelling dictionaries. If the name argument is omitted Swirc\n" "will output a list of available dictionaries. The list is obtained\n" "remotely which means its contents can be updated at any time." msgstr "" #: ../src/include/commandhelp.h:276 msgid "Fetch the american english dictionary:" msgstr "" #: ../src/include/commandhelp.h:295 msgid "" "Retrieve and store files on a FTP server. (The communication is done\n" "in clear text, i.e. unencrypted.)" msgstr "" #: ../src/include/commandhelp.h:301 msgid "usage: /gline [ :]" msgstr "" #: ../src/include/commandhelp.h:303 msgid "Network-wide bans." msgstr "" #: ../src/include/commandhelp.h:305 msgid "" "When a client matches a G-line it cannot connect to ANY server on the\n" "IRC network for 'duration' seconds. If the duration is zero then the\n" "G-line will be permanent." msgstr "" #: ../src/include/commandhelp.h:309 msgid "(If no duration and no reason is given the G-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:318 msgid "usage: /group " msgstr "" #: ../src/include/commandhelp.h:320 msgid "Changes ICB group." msgstr "" #: ../src/include/commandhelp.h:325 msgid "usage: /help [command]" msgstr "" #: ../src/include/commandhelp.h:327 msgid "Outputs a list of all available commands." msgstr "" #: ../src/include/commandhelp.h:328 msgid "(Or help for a specific command.)" msgstr "" #: ../src/include/commandhelp.h:333 msgid "usage: /ignore [regex]" msgstr "" #: ../src/include/commandhelp.h:335 msgid "" "Ignores all 'nick!user@host' that matches the given regular\n" "expression, this by using the POSIX basic regular expression\n" "grammar. This command isn't to be used by beginners and I advice you\n" "to be careful when using it. I highly recommend the use of:" msgstr "" #: ../src/include/commandhelp.h:340 msgid " 1. ^ Matches the starting position within the string, if it is" msgstr "" #: ../src/include/commandhelp.h:341 msgid " the first character of the regular expression." msgstr "" #: ../src/include/commandhelp.h:342 msgid " 2. $ Matches the ending position of the string, if it is the last" msgstr "" #: ../src/include/commandhelp.h:343 msgid " character of the regular expression." msgstr "" #: ../src/include/commandhelp.h:347 msgid "Ignore nickname 'troll':" msgstr "" #: ../src/include/commandhelp.h:350 msgid "Ignore all users with username 'troll':" msgstr "" #: ../src/include/commandhelp.h:353 msgid "Ignore all users with hostname 'insecure.org':" msgstr "" #: ../src/include/commandhelp.h:356 msgid "Ignore all users with a Chinese domain (.cn):" msgstr "" #: ../src/include/commandhelp.h:362 msgid "usage: /ilist [channel]" msgstr "" #: ../src/include/commandhelp.h:364 msgid "" "Outputs a channel's invitation list. An invitation mask (+I) overrides\n" "the invite-only flag (+i). If the channel argument is omitted and the\n" "active window is an IRC channel, the client will output the invitation\n" "list for that channel." msgstr "" #: ../src/include/commandhelp.h:372 msgid "usage: /info [target]" msgstr "" #: ../src/include/commandhelp.h:374 msgid "The info command returns information about the server." msgstr "" #: ../src/include/commandhelp.h:379 msgid "usage: /invite " msgstr "" #: ../src/include/commandhelp.h:381 msgid "Invites 'targ_nick' to a channel." msgstr "" #: ../src/include/commandhelp.h:386 msgid "usage: /ison [nick2] [nick3] [...]" msgstr "" #: ../src/include/commandhelp.h:388 msgid "Checks whether users are on IRC." msgstr "" #: ../src/include/commandhelp.h:393 msgid "usage: /join [key]" msgstr "" #: ../src/include/commandhelp.h:395 msgid "Joins a channel (optionally by using a key)." msgstr "" #: ../src/include/commandhelp.h:399 msgid "Join a channel with name 'libera':" msgstr "" #: ../src/include/commandhelp.h:402 msgid "Join a key-protected channel:" msgstr "" #: ../src/include/commandhelp.h:408 msgid "usage: /kick [reason]" msgstr "" #: ../src/include/commandhelp.h:410 msgid "" "Kicks one or more users out of a channel. The users are given in a\n" "comma-separated list. A reason is optional and the active window must\n" "be an IRC channel." msgstr "" #: ../src/include/commandhelp.h:417 msgid "usage: /kickban [reason]" msgstr "" #: ../src/include/commandhelp.h:419 msgid "" "Set a channel ban with given 'mask' and kick the user 'nick' out of a\n" "channel. A reason is optional and the active window must be an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:426 msgid "usage: /kill " msgstr "" #: ../src/include/commandhelp.h:428 msgid "Disconnect a user from the connected network." msgstr "" #: ../src/include/commandhelp.h:429 msgid "(Requires IRC op privilege.)" msgstr "" #: ../src/include/commandhelp.h:434 msgid "usage: /kline [ :]" msgstr "" #: ../src/include/commandhelp.h:436 msgid "Server-local bans." msgstr "" #: ../src/include/commandhelp.h:438 msgid "" "When a client matches a K-line it cannot connect to the local server\n" "for 'duration' seconds. If the duration is zero then the K-line will\n" "be permanent." msgstr "" #: ../src/include/commandhelp.h:442 msgid "(If no duration and no reason is given the K-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:451 msgid "usage: /list [min_users][,pattern][...]]" msgstr "" #: ../src/include/commandhelp.h:453 msgid "" "Lists channels and their topics. Without any arguments the list is\n" "HUGE. For example, '/list >1500' will only list channels that have\n" "more than 1500 users." msgstr "" #: ../src/include/commandhelp.h:457 msgid "" "Depending on the IRC server software used by your network the usage\n" "may differ." msgstr "" #: ../src/include/commandhelp.h:463 msgid "usage: /me " msgstr "" #: ../src/include/commandhelp.h:465 msgid "Send an 'action' message. (Used to simulate role playing on IRC.)" msgstr "" #: ../src/include/commandhelp.h:470 msgid "usage: /mode [...]" msgstr "" #: ../src/include/commandhelp.h:472 msgid "Alter modes." msgstr "" #: ../src/include/commandhelp.h:476 msgid " o - give/take the channel operator privilege" msgstr "" #: ../src/include/commandhelp.h:477 msgid " v - give/take the channel voice privilege" msgstr "" #: ../src/include/commandhelp.h:479 msgid " i - invite-only channel" msgstr "" #: ../src/include/commandhelp.h:480 msgid " m - moderated channel" msgstr "" #: ../src/include/commandhelp.h:481 msgid " n - no messages to a channel from clients on the outside" msgstr "" #: ../src/include/commandhelp.h:482 msgid " p - private channel" msgstr "" #: ../src/include/commandhelp.h:483 msgid " s - secret channel" msgstr "" #: ../src/include/commandhelp.h:484 msgid " t - topic settable by channel operators only" msgstr "" #: ../src/include/commandhelp.h:486 msgid " k - set/remove the channel key (password)" msgstr "" #: ../src/include/commandhelp.h:487 msgid " l - set/remove the channel user limit" msgstr "" #: ../src/include/commandhelp.h:489 msgid " b - set/remove a ban mask" msgstr "" #: ../src/include/commandhelp.h:490 msgid " e - set/remove an exception mask to override a ban mask" msgstr "" #: ../src/include/commandhelp.h:491 msgid " I - set/remove an invitation mask to override the invite-only flag" msgstr "" #: ../src/include/commandhelp.h:495 msgid " i - marks a user as invisible" msgstr "" #: ../src/include/commandhelp.h:496 msgid " w - the user receives wallops messages" msgstr "" #: ../src/include/commandhelp.h:500 msgid " Give channel operator privilege to 'Companion' in #foo:" msgstr "" #: ../src/include/commandhelp.h:503 msgid " Restrict messaging to channel #linux:" msgstr "" #: ../src/include/commandhelp.h:506 msgid " Limit user count for #freenode to 10:" msgstr "" #: ../src/include/commandhelp.h:509 msgid " Deny all users with hostname spammers.net from joining #chatzone:" msgstr "" #: ../src/include/commandhelp.h:512 msgid " Turn on reception of WALLOPS messages:" msgstr "" #: ../src/include/commandhelp.h:518 msgid "usage: /msg " msgstr "" #: ../src/include/commandhelp.h:520 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels." msgstr "" #: ../src/include/commandhelp.h:526 msgid "usage: /nick " msgstr "" #: ../src/include/commandhelp.h:528 msgid "Sets your nickname." msgstr "" #: ../src/include/commandhelp.h:533 msgid "usage: /nickserv <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:535 msgid "Communicate with your IRC network's nickname service." msgstr "" #: ../src/include/commandhelp.h:537 msgid "If the initial argument equals to '--' then the:" msgstr "" #: ../src/include/commandhelp.h:539 msgid " 1) Value of setting 'nickserv_host' will be used as service" msgstr "" #: ../src/include/commandhelp.h:540 msgid " hostname." msgstr "" #: ../src/include/commandhelp.h:541 msgid " 2) Command call won't be added to the command history provided" msgstr "" #: ../src/include/commandhelp.h:542 msgid " that the second argument is 'identify'." msgstr "" #: ../src/include/commandhelp.h:544 msgid "" "The correct service hostname is not always the same as the visible\n" "hostname of NickServ. FYI at the AnonOps IRC network the visible\n" "hostname of NickServ is anonops.in (when this text was written)\n" "but you should use 'services.anonops.com'. As a fallback:" msgstr "" #: ../src/include/commandhelp.h:548 msgid "" "'/query NickServ' can be used in order to communicate with the\n" "service." msgstr "" #: ../src/include/commandhelp.h:554 msgid "usage: /notice " msgstr "" #: ../src/include/commandhelp.h:556 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels. (In notice form.)" msgstr "" #: ../src/include/commandhelp.h:562 msgid "usage: /op " msgstr "" #: ../src/include/commandhelp.h:564 msgid "Gives the channel operator privilege to another user." msgstr "" #: ../src/include/commandhelp.h:569 msgid "usage: /oper " msgstr "" #: ../src/include/commandhelp.h:571 msgid "Identifies yourself as an IRC operator." msgstr "" #: ../src/include/commandhelp.h:576 msgid "usage: /part [channel] [message]" msgstr "" #: ../src/include/commandhelp.h:578 msgid "" "Leaves a channel (optionally with a message). If the command is called\n" "without any arguments and the current window is an IRC channel, that\n" "channel will be the target." msgstr "" #: ../src/include/commandhelp.h:584 msgid " Leave channel #chatzone with message 'bye':" msgstr "" #: ../src/include/commandhelp.h:585 msgid " /part #chatzone bye" msgstr "" #: ../src/include/commandhelp.h:590 msgid "usage: /passmod " msgstr "" #: ../src/include/commandhelp.h:592 msgid "Pass the ICB moderation privilege to another group member." msgstr "" #: ../src/include/commandhelp.h:597 msgid "usage: /qbot <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:599 msgid "" "Communicate with QuakeNet's network service, the Q bot. If the initial\n" "argument equals to '--', then the value of setting 'qbot_host' is used\n" "as a service hostname." msgstr "" #: ../src/include/commandhelp.h:606 msgid "usage: /query [nick]" msgstr "" #: ../src/include/commandhelp.h:608 msgid "" "Starts a private conversation with 'nick'. If 'nick' is omitted and\n" "the active window is a private conversation, the action will be to\n" "close it." msgstr "" #: ../src/include/commandhelp.h:615 msgid "usage: /quit [message]" msgstr "" #: ../src/include/commandhelp.h:617 msgid "" "Disconnect from IRC and quit the program. A disconnect message is\n" "optional." msgstr "" #: ../src/include/commandhelp.h:623 msgid "usage: /rehash" msgstr "" #: ../src/include/commandhelp.h:625 msgid "" "The rehash command is an administrative command which can be used by\n" "an IRC operator to force the server to re-read and process its conf-\n" "iguration file." msgstr "" #: ../src/include/commandhelp.h:632 msgid "usage: /resize" msgstr "" #: ../src/include/commandhelp.h:634 msgid "" "Resize the terminal. For example, Windows doesn't send 'SIGWINCH',\n" "instead this command can be used. First resize the window then run\n" "this command." msgstr "" #: ../src/include/commandhelp.h:641 msgid "usage: /restart [--I-am-sure]" msgstr "" #: ../src/include/commandhelp.h:643 msgid "" "An IRC operator can use the 'restart'-command to force the server to\n" "restart itself." msgstr "" #: ../src/include/commandhelp.h:649 msgid "usage: /rules" msgstr "" #: ../src/include/commandhelp.h:651 msgid "" "Outputs network/server rules. Not all IRC server software supports\n" "this command. (It's actually quite rare.)" msgstr "" #: ../src/include/commandhelp.h:657 msgid "usage: /sasl [...]" msgstr "" #: ../src/include/commandhelp.h:659 msgid "Simple Authentication and Security Layer." msgstr "" #: ../src/include/commandhelp.h:660 msgid "Operation can be either:" msgstr "" #: ../src/include/commandhelp.h:672 msgid "" "SASL is a method that lets you identify with NickServ during the\n" "connection process eliminating the need to do it manually." msgstr "" #: ../src/include/commandhelp.h:674 msgid "(To use SASL you must register your nickname.)" msgstr "" #: ../src/include/commandhelp.h:678 msgid " Identification using mechanism ecdsa-nist256p-challenge:" msgstr "" #: ../src/include/commandhelp.h:686 msgid "" " (The only IRC network that I know of that is supporting this\n" " mechanism is Libera Chat.)" msgstr "" #: ../src/include/commandhelp.h:689 msgid " Identification using mechanism 'plain':" msgstr "" #: ../src/include/commandhelp.h:698 msgid "usage: /say " msgstr "" #: ../src/include/commandhelp.h:700 msgid "" "Say a message. This command can be used if you want say something with\n" "a leading command-character, i.e. a slash." msgstr "" #: ../src/include/commandhelp.h:705 msgid " /say // A single-line comment in C++" msgstr "" #: ../src/include/commandhelp.h:710 msgid "usage: /servlist [ []]" msgstr "" #: ../src/include/commandhelp.h:712 msgid "" "Lists services currently connected to your IRC network. Arguments,\n" "if given, can be used to restrict the output result." msgstr "" #: ../src/include/commandhelp.h:722 msgid "usage: /servstats [ []]" msgstr "" #: ../src/include/commandhelp.h:724 msgid "This command is used to query statistics of a certain IRC server." msgstr "" #: ../src/include/commandhelp.h:729 msgid "usage: /set [[setting] [value]]" msgstr "" #: ../src/include/commandhelp.h:731 msgid "Alter Swirc settings." msgstr "" #: ../src/include/commandhelp.h:736 msgid "" " Bools are case insensitive and can have one of the following\n" " values:" msgstr "" #: ../src/include/commandhelp.h:738 msgid " - on, true or yes" msgstr "" #: ../src/include/commandhelp.h:739 msgid " - off, false or no" msgstr "" #: ../src/include/commandhelp.h:742 msgid "" " Integers. Swirc implements a min/max value for each integer in\n" " order to keep its value safe. The error log will tell if the\n" " restrictions for an integer aren't within limits and, if so, that\n" " a preprogrammed fallback value is being used instead." msgstr "" #: ../src/include/commandhelp.h:748 msgid " An arbitrary sequence of characters" msgstr "" #: ../src/include/commandhelp.h:752 msgid " Output the current values of all settings:" msgstr "" #: ../src/include/commandhelp.h:753 msgid " /set (without any arguments)" msgstr "" #: ../src/include/commandhelp.h:755 msgid " Turn beeps on/off:" msgstr "" #: ../src/include/commandhelp.h:762 msgid "usage: /squery " msgstr "" #: ../src/include/commandhelp.h:764 msgid "" "This command is used similarly to '/msg'. The only difference is that\n" "the recipient MUST be a service." msgstr "" #: ../src/include/commandhelp.h:770 msgid "usage: /stats [channel]" msgstr "" #: ../src/include/commandhelp.h:772 msgid "" "Outputs a channel's user statistics. If the channel argument is left\n" "empty and the active window is an IRC channel, Swirc will output the\n" "user statistics for that channel." msgstr "" #: ../src/include/commandhelp.h:779 msgid "usage: /theme [install | list-remote | set ]" msgstr "" #: ../src/include/commandhelp.h:781 msgid "Management of themes on-the-fly." msgstr "" #: ../src/include/commandhelp.h:785 msgid " Install a theme named 'bx':" msgstr "" #: ../src/include/commandhelp.h:788 msgid " List all available themes:" msgstr "" #: ../src/include/commandhelp.h:791 msgid " Activate an installed theme with name 'bx':" msgstr "" #: ../src/include/commandhelp.h:797 msgid "usage: /time " msgstr "" #: ../src/include/commandhelp.h:799 msgid "" "Send a CTCP TIME request to 'target', which is either a nickname or an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:805 msgid "usage: /topic [new topic]" msgstr "" #: ../src/include/commandhelp.h:807 msgid "" "Sets a new topic for a channel. If 'new topic' is omitted the action\n" "will be to display the current topic. (The active window must be an\n" "IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:814 msgid "usage: /unban " msgstr "" #: ../src/include/commandhelp.h:816 msgid "Removes a channel ban. (The active window must be an IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:826 msgid "usage: /unignore [#]" msgstr "" #: ../src/include/commandhelp.h:828 msgid "Deletes a regular expression from the ignore list." msgstr "" #: ../src/include/commandhelp.h:837 msgid "usage: /version " msgstr "" #: ../src/include/commandhelp.h:839 msgid "" "Send a CTCP VERSION request to 'target', which is either a nickname or\n" "an IRC channel." msgstr "" #: ../src/include/commandhelp.h:845 msgid "usage: /voice " msgstr "" #: ../src/include/commandhelp.h:847 msgid "Gives the channel voice privilege to another user." msgstr "" #: ../src/include/commandhelp.h:852 msgid "usage: /wallops " msgstr "" #: ../src/include/commandhelp.h:854 msgid "" "The 'wallops'-command is used to send a message to all currently\n" "connected users who have set the 'w' user mode for themselves." msgstr "" #: ../src/include/commandhelp.h:860 msgid "usage: /who " msgstr "" #: ../src/include/commandhelp.h:862 msgid "" "Generates a query which returns a list of information which matches\n" "the provided 'mask'." msgstr "" #: ../src/include/commandhelp.h:867 msgid " Show the Libera Chat crew:" msgstr "" #: ../src/include/commandhelp.h:870 msgid " Show users with a German domain name:" msgstr "" #: ../src/include/commandhelp.h:876 msgid "usage: /whois " msgstr "" #: ../src/include/commandhelp.h:878 msgid "Asks after information about another user." msgstr "" #: ../src/include/commandhelp.h:883 msgid "usage: /wholeft" msgstr "" #: ../src/include/commandhelp.h:885 msgid "" "By using this command you can see who left during a network server\n" "split. The command takes no arguments and the active window must be an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:892 msgid "usage: /znc [*module] " msgstr "" #: ../src/include/commandhelp.h:894 msgid "" "Simplifies the communication with ZNC which is a popular\n" "'IRC bouncer'." msgstr "" #: ../src/include/commandhelp.h:899 msgid " Output your ZNC version:" msgstr "" #: ../src/include/commandhelp.h:902 msgid " Identical to the previous example:" msgstr "" #: ../src/interpreter.cpp:56 ../src/interpreter.cpp:87 #, c-format msgid "%s: fatal: string was truncated" msgstr "" #: ../src/io-loop.c:596 #, c-format msgid " Swirc %s by %s" msgstr " Swirc %s von %s" #: ../src/io-loop.c:597 #, c-format msgid " Compiled on %s%s %s%s" msgstr " Kompiliert %s%s %s%s" #: ../src/io-loop.c:603 #, c-format msgid "Current language %s%s%s" msgstr "Aktuelle Sprache %s%s%s" #: ../src/io-loop.c:606 #, c-format msgid "Program settings are stored in %s%s%s" msgstr "Programmeinstellungen werden in %s%s%s gespeichert" #: ../src/io-loop.c:608 #, c-format msgid "%c%hd%c color pairs have been initialized" msgstr "Es wurden %c%hd%c Farbpaare initialisiert" #: ../src/io-loop.c:610 msgid "Type /help for a list of commands; or /help " msgstr "Geben Sie /help für eine Liste von Befehlen ein; oder /help " #: ../src/io-loop.c:612 msgid "for help of a specific command" msgstr "für Hilfe zu einem bestimmten Befehl" #: ../src/io-loop.c:613 msgid "Type F1 for keys" msgstr "Geben Sie F1 für Tastaturtasten ein" #: ../src/io-loop.c:615 #, c-format msgid "Error log size %s%.1f KB%s" msgstr "Größe des Fehlerprotokolls %s%.1f KB%s" #: ../src/irc.c:332 #, c-format msgid "%s: no nickname" msgstr "" #: ../src/irc.c:451 #, c-format msgid "%s: unsupported extension" msgstr "" #: ../src/irc.c:648 #, c-format msgid "Unknown normal event: %s" msgstr "" #: ../src/irc.c:659 #, c-format msgid "Unknown numeric event: %s" msgstr "" #: ../src/irc.c:667 #, c-format msgid "Erroneous event: %s" msgstr "" #: ../src/log.c:217 #, c-format msgid "Logging for window (refnum: %d) now off" msgstr "Die Protokollierung für das Fenster (refnum: %d) ist jetzt deaktiviert" #: ../src/log.c:221 #, c-format msgid "Logging for window (refnum: %d) now on" msgstr "Die Protokollierung für das Fenster (refnum: %d) ist jetzt aktiviert" #: ../src/main.cpp:84 #, c-format msgid "A duplicate of option -%c found" msgstr "" #: ../src/main.cpp:144 msgid "" "\n" "Options:\n" "\n" msgstr "" "\n" "Optionen:\n" "\n" #: ../src/main.cpp:145 msgid " -4 Use IPv4 addresses only\n" msgstr " -4 Nur IPv4-Adressen verwenden\n" #: ../src/main.cpp:146 msgid " -6 Use IPv6 addresses only\n" msgstr " -6 Nur IPv6-Adressen verwenden\n" #: ../src/main.cpp:147 msgid " -?, --help Output help\n" msgstr " -?, --help Hilfe anzeigen\n" #: ../src/main.cpp:148 msgid " -C Do not change color definitions\n" msgstr " -C Farbdefinitionen nicht ändern\n" #: ../src/main.cpp:149 msgid " -P Permanently disable SASL authentication\n" msgstr "" " -P SASL-Authentifizierung dauerhaft deaktivieren\n" #: ../src/main.cpp:150 msgid " -R Disable TLS/SSL peer verification\n" msgstr " -R TLS/SSL peer verification deaktivieren\n" #: ../src/main.cpp:151 msgid " -S Force TLS\n" msgstr " -S TLS erzwingen\n" #: ../src/main.cpp:156 msgid " -W Equal effect as flag 'p' but non-interactive\n" msgstr "" " -W Gleicher Effekt wie Flag 'p', aber nicht " "interaktiv\n" #: ../src/main.cpp:157 msgid " -X Disable all IRCv3 extensions\n" msgstr " -X Alle IRCv3 Erweiterungen deaktivieren\n" #: ../src/main.cpp:158 msgid " -c Connect to IRC server\n" msgstr " -c Mit IRC-Server verbinden\n" #: ../src/main.cpp:159 msgid " -d Debug logging\n" msgstr " -d Debug Protokollierung\n" #: ../src/main.cpp:160 msgid " -i Turn on Internet Citizen's Band mode\n" msgstr " -i Internet Citizen's Band aktivieren\n" #: ../src/main.cpp:161 msgid " -j A comma-separated list of channels to join\n" msgstr "" " -j Kommata getrennte Liste von Kanälen zum joinen\n" #: ../src/main.cpp:162 msgid " -n Online nickname\n" msgstr " -n Online Nickname\n" #: ../src/main.cpp:163 msgid "" " -p Query for server password (for private servers)\n" msgstr "" " -p Server-Passwort anfordern (für private Server)\n" #: ../src/main.cpp:164 msgid " -r Your real name\n" msgstr " -r Dein echter Name\n" #: ../src/main.cpp:165 msgid " -u Your username\n" msgstr " -u Dein Benutzername\n" #: ../src/main.cpp:166 msgid " -v, --version Output Swirc version\n" msgstr " -v, --version Swirc-Version anzeigen\n" #: ../src/main.cpp:167 msgid " -x Config file\n" msgstr " -x Konfigurationsdatei\n" #: ../src/main.cpp:230 msgid "Unhandled exception!" msgstr "" #: ../src/main.cpp:290 #, c-format msgid "Usage: %s [OPTION] ...\n" msgstr "Anwendung: %s [FLAGGE] ...\n" #: ../src/main.cpp:380 msgid "forbidden channel name characters" msgstr "" #: ../src/main.cpp:532 #, c-format msgid "%s: -%c: unrecognized option" msgstr "" #: ../src/main.cpp:537 #, c-format msgid "%s: -%c: option argument missing" msgstr "" #: ../src/main.cpp:706 msgid "fatal: failed to initialize signal handling" msgstr "" #: ../src/main.cpp:728 msgid "fatal: running the program with root privileges is forbidden" msgstr "" #: ../src/main.cpp:750 msgid "Initialization of the Curses library not possible" msgstr "" #: ../src/messagetags.c:150 #, c-format msgid "%s: server time error" msgstr "" #: ../src/nestHome.c:143 #, c-format msgid "%s exists. However; it isn't a directory." msgstr "" #: ../src/nestHome.c:147 ../src/nestHome.c:150 msgid "Cannot make directory" msgstr "" #: ../src/nestHome.c:177 msgid "Please decrypt your SASL password..." msgstr "" #: ../src/nestHome.c:178 msgid "(One attempt)" msgstr "" #: ../src/nestHome.c:186 msgid "Password: " msgstr "" #: ../src/nestHome.c:216 msgid "Decryption failed" msgstr "" #: ../src/nestHome.c:219 msgid "Wrong password" msgstr "" #: ../src/nestHome.c:230 msgid "The password seems reasonable" msgstr "" #: ../src/nestHome.c:242 msgid "Press " msgstr "" #: ../src/nestHome.c:267 ../src/nestHome.c:278 ../src/nestHome.c:337 #, c-format msgid "%s exists -- but isn't a regular file." msgstr "" #: ../src/nestHome.c:270 ../src/nestHome.c:345 #, c-format msgid "%s no such file or directory. Exiting..." msgstr "" #: ../src/nestHome.c:301 msgid "Warning!" msgstr "" #: ../src/nestHome.c:302 msgid "" "A decrypted SASL password was found in the read configuration file -- " "cannot continue!" msgstr "" #: ../src/nestHome.c:333 msgid "The item 'theme' in the user configuration file holds no data. Error." msgstr "" #: ../src/nestHome.c:358 msgid "Can't resolve the home path!" msgstr "" #: ../src/network.cpp:183 msgid "Hostname checking failed!" msgstr "Hostnamenprüfung fehlgeschlagen!" #: ../src/network.cpp:185 msgid "Hostname checking OK!" msgstr "Hostnamenprüfung OK!" #: ../src/network.cpp:242 msgid "Connected!" msgstr "In Verbindung gebracht!" #: ../src/network.cpp:311 msgid "Get a list of IP addresses completed" msgstr "Holen Sie sich eine Liste der IP-Adressen, fertig" #: ../src/network.cpp:539 #, c-format msgid "Connecting to %s (%s)" msgstr "Verbindung zu %s (%s) wird hergestellt..." #: ../src/network.cpp:807 msgid "Connection to IRC server lost" msgstr "Die Verbindung zum IRC-Server wurde unterbrochen" #: ../src/network.cpp:818 msgid "Disconnected" msgstr "Offline" #: ../src/readline.c:623 msgid "--------------- Keys ---------------" msgstr "" #: ../src/readline.c:625 msgid "CTRL+a Move to beginning of line" msgstr "" #: ../src/readline.c:626 msgid "CTRL+e Move to end of line" msgstr "" #: ../src/readline.c:627 msgid "CTRL+b Move cursor backward" msgstr "" #: ../src/readline.c:628 msgid "CTRL+f Move cursor forward" msgstr "" #: ../src/readline.c:629 msgid "CTRL+d Delete" msgstr "" #: ../src/readline.c:630 msgid "CTRL+g Clear readline input" msgstr "" #: ../src/readline.c:631 msgid "CTRL+l Toggle logging on/off" msgstr "" #: ../src/readline.c:632 msgid "CTRL+n Next window" msgstr "" #: ../src/readline.c:633 msgid "CTRL+p Previous window" msgstr "" #: ../src/readline.c:634 msgid "CTRL+w List all windows" msgstr "" #: ../src/readline.c:635 msgid "PG UP Scroll up" msgstr "" #: ../src/readline.c:636 msgid "PG DOWN Scroll down" msgstr "" #: ../src/readline.c:637 msgid "Up arrow History previous" msgstr "" #: ../src/readline.c:638 msgid "Down arrow History next" msgstr "" #: ../src/readline.c:639 msgid "F2 Spell word" msgstr "" #: ../src/readline.c:640 msgid "F3 Scroll nicklist up" msgstr "" #: ../src/readline.c:641 msgid "F4 Scroll nicklist down" msgstr "" #: ../src/readline.c:642 msgid "F11 Close window" msgstr "" #: ../src/readline.c:643 msgid "F12 Close all private conversations" msgstr "" #: ../src/sig-w32.c:26 msgid "Abnormal termination" msgstr "Abgebrochen" #: ../src/sig-w32.c:27 msgid "Floating-point error" msgstr "Gleitkomma-Ausnahme" #: ../src/sig-w32.c:28 msgid "Illegal instruction" msgstr "Ungültiger Maschinenbefehl" #: ../src/sig-w32.c:29 msgid "Illegal storage access" msgstr "Speicherzugriffsfehler" #: ../src/sig-w32.c:30 msgid "Termination request" msgstr "Beendet" #: ../src/spell.cpp:137 ../src/spell.cpp:141 #, c-format msgid "%s: %s not found" msgstr "%s: %s nicht gefunden" #: ../src/spell.cpp:317 msgid "no more suggestions" msgstr "keine Vorschläge mehr" #: ../src/spell.cpp:344 msgid "suggestions:" msgstr "Vorschläge:" #: ../src/spell.cpp:372 #, c-format msgid "%ls is correct" msgstr "%ls ist korrekt" #: ../src/spell.cpp:377 #, c-format msgid "%ls is incorrect" msgstr "%ls ist falsch" #: ../src/statusbar.cpp:222 msgid "Log: " msgstr "Log: " #: ../src/statusbar.cpp:227 msgid "-- MORE --" msgstr "-- MEHR --" #: ../src/tls-server.cpp:205 msgid "Already accepting DCC connections..." msgstr "Es werden bereits DCC Verbindungen akzeptiert..." #: ../src/tls-server.cpp:229 #, c-format msgid "Accepting DCC connections at port: %d" msgstr "DCC Verbindungen werden am Port akzeptiert: %d" #: ../src/tls-server.cpp:241 msgid "Out of memory" msgstr "Nicht genügend Arbeitsspeicher" #: ../src/tls-server.cpp:250 msgid "Stopped accepting DCC connections" msgstr "DCC Verbindungen werden nicht mehr akzeptiert" swirc-3.5.5/po/fi/000077500000000000000000000000001501213070300136575ustar00rootroot00000000000000swirc-3.5.5/po/fi/swirc.po000066400000000000000000001516311501213070300153550ustar00rootroot00000000000000# Finnish translations for swirc package # Suomenkielinen käännös swirc-paketille. # This file is put in the public domain. # , 2021. # msgid "" msgstr "" "Project-Id-Version: swirc 3.5.5\n" "Report-Msgid-Bugs-To: https://github.com/uhlin/swirc/issues\n" "POT-Creation-Date: 2025-05-17 12:31+0200\n" "PO-Revision-Date: 2025-05-17 12:18+0200\n" "Last-Translator: Markus Uhlin \n" "Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/commands/colormap.cpp:153 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:222 msgid "Yes" msgstr "Kyllä" #: ../src/commands/colormap.cpp:154 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:223 msgid "No" msgstr "Ei" #: ../src/commands/connect.c:211 msgid "Connect using password? [Y/n]: " msgstr "Yhdistä salasanalla? [Y/n]: " #: ../src/commands/connect.c:219 ../src/commands/connect.c:280 #: ../src/commands/connect.c:327 ../src/nestHome.c:202 msgid "input too big" msgstr "käyttäjän syöte liian suuri" #: ../src/commands/connect.c:248 #, c-format msgid "%s: string copy error" msgstr "%s: merkkijonon kopiointivirhe" #: ../src/commands/connect.c:264 msgid "Server password (will not echo): " msgstr "Palvelimen salasana (ei kaiku): " #: ../src/commands/connect.c:317 #, c-format msgid "Your choice (0-%d): " msgstr "Valintasi (0-%d): " #: ../src/commands/connect.c:520 #, c-format msgid "Next reconnect attempt in %ld seconds..." msgstr "Seuraava uudelleenyhteysyritys %ld sekunnin kuluttua..." #: ../src/commands/connect.c:532 msgid "Reconnection aborted!" msgstr "Uudelleenkytkentä keskeytetty!" #: ../src/commands/connect.c:572 msgid "Cannot initiate use of the Winsock DLL" msgstr "Winsock DLL:n käyttöä ei voi aloittaa" #: ../src/commands/connect.c:576 msgid "Use of the Winsock DLL granted" msgstr "Winsock DLL:n käyttö myönnetty" #: ../src/commands/ctcp.c:77 ../src/commands/ftp.cpp:873 #: ../src/commands/ftp.cpp:960 ../src/commands/ftp.cpp:1009 #: ../src/commands/ftp.cpp:1046 ../src/commands/ftp.cpp:1128 msgid "Insufficient arguments" msgstr "Riittämättömät argumentit" #: ../src/commands/ctcp.c:86 #, c-format msgid "%s: insufficient arguments" msgstr "%s: riittämättömät argumentit" #: ../src/commands/ctcp.c:89 #, c-format msgid "%s: invalid target" msgstr "%s: virheellinen kohde" #: ../src/commands/ctcp.c:94 #, c-format msgid "%s: forbidden channel name characters" msgstr "%s: kielletyt kanavan nimen merkit" #: ../src/commands/ctcp.c:105 msgid "Invalid CTCP query" msgstr "Virheellinen CTCP kysely" #: ../src/commands/dcc.cpp:212 msgid "Write error" msgstr "Tiedoston kirjoitusvirhe" #: ../src/commands/dcc.cpp:337 #, c-format msgid "%s: wrote: %s" msgstr "%s: kirjoitti: %s" #: ../src/commands/dcc.cpp:340 #, c-format msgid "%s: did not complete: %s" msgstr "%s: ei suoritettu loppuun: %s" #: ../src/commands/dcc.cpp:1017 #, c-format msgid "%sStarted%s: %s" msgstr "%sAlkoi%s: %s" #: ../src/commands/dcc.cpp:1019 #, c-format msgid "%sStopped%s: %s" msgstr "%sLopetti%s: %s" #: ../src/commands/dcc.cpp:1393 #, c-format msgid "sending '%s' to %s (%.1f%c)..." msgstr "lähetetään '%s' osoitteeseen %s (%.1f%c)..." #: ../src/commands/dcc.cpp:1484 msgid "Missing certs. Not starting the DCC server. Please create them." msgstr "" "SSL sertifikaatit puuttuvat. DCC palvelinta ei käynnistetä. Ole hyvä ja luo " "ne." #: ../src/commands/dcc.cpp:1590 #, c-format msgid "%s: added: '%s' (%.1f%c)" msgstr "%s: lisätty: '%s' (%.1f%c)" #: ../src/commands/dcc.cpp:1592 #, c-format msgid "%s: from: %s <%s@%s>" msgstr "%s: lähettäjä: %s <%s@%s>" #: ../src/commands/dcc.cpp:1594 msgid "To get the file, type:" msgstr "Saadaksesi tiedoston, kirjoita:" #: ../src/commands/dcc.cpp:1845 ../src/commands/ftp.cpp:805 #, c-format msgid "%s: file read error" msgstr "%s: tiedostolukuvirhe" #: ../src/commands/dcc.cpp:1850 #, c-format msgid "%s: tls write error" msgstr "%s: tls kirjoitusvirhe" #: ../src/commands/dcc.cpp:1867 #, c-format msgid "started: %s" msgstr "alkoi: %s" #: ../src/commands/dcc.cpp:1868 #, c-format msgid "stopped: %s" msgstr "lopetti: %s" #: ../src/commands/dcc.cpp:1885 msgid "read request error" msgstr "lukupyynnön virhe" #: ../src/commands/dcc.cpp:1892 msgid "unable to find the send object" msgstr "lähetysobjektia ei löydy" #: ../src/commands/dcc.cpp:1900 msgid "already sent file" msgstr "tiedosto on jo lähetetty" #: ../src/commands/dcc.cpp:1903 msgid "the send object is in a locked state" msgstr "lähetysobjekti on lukitussa tilassa" #: ../src/commands/dcc.cpp:1908 msgid "file open error" msgstr "tiedoston avaamisvirhe" #: ../src/commands/dcc.cpp:1927 #, c-format msgid "%s: successfully sent file: %s" msgstr "%s: onnistuneesti lähetetty tiedosto: %s" #: ../src/commands/dcc.cpp:1931 #, c-format msgid "%s: file transfer incomplete: %s" msgstr "%s: tiedostonsiirto keskeneräinen: %s" #: ../src/commands/ftp.cpp:181 msgid "Invalid username" msgstr "Virheellinen käyttäjänimi" #: ../src/commands/ftp.cpp:183 msgid "Too long password" msgstr "Liian pitkä salasana" #: ../src/commands/ftp.cpp:185 msgid "Invalid hostname" msgstr "Virheellinen isäntänimi" #: ../src/commands/ftp.cpp:187 msgid "Invalid port number" msgstr "Virheellinen portin numero" #: ../src/commands/ftp.cpp:190 ../src/network.cpp:308 msgid "Unable to get a list of IP addresses" msgstr "IP osoitteiden luetteloa ei voi saada" #: ../src/commands/ftp.cpp:193 ../src/commands/ftp.cpp:533 #: ../src/network.cpp:175 msgid "Failed to establish a connection" msgstr "Yhteyden muodostaminen epäonnistui" #: ../src/commands/ftp.cpp:204 ../src/commands/ftp.cpp:886 #: ../src/commands/ftp.cpp:1257 msgid "Cannot send" msgstr "Ei voi lähettää" #: ../src/commands/ftp.cpp:521 msgid "Failed to create an endpoint for communication" msgstr "Viestinnän päätepisteen luominen epäonnistui" #: ../src/commands/ftp.cpp:813 #, c-format msgid "%s: bytes sent mismatch bytes read" msgstr "%s: lähetetyt tavut eivät täsmää luettuina" #: ../src/commands/ftp.cpp:876 ../src/commands/ftp.cpp:1245 msgid "No control connection" msgstr "Ei ohjausliitäntää" #: ../src/commands/ftp.cpp:879 ../src/commands/ftp.cpp:901 #: ../src/commands/ftp.cpp:1253 msgid "Invalid network socket" msgstr "Virheellinen verkkoliitäntä" #: ../src/commands/ftp.cpp:963 ../src/commands/ftp.cpp:1049 msgid "Blank file path" msgstr "Tyhjä tiedostopolku" #: ../src/commands/ftp.cpp:1249 msgid "Already in passive mode" msgstr "Jo passiivisessa tilassa" #: ../src/commands/ftp.cpp:1272 msgid "Failed to enter passive mode" msgstr "Passiiviseen tilaan siirtyminen epäonnistui" #: ../src/commands/theme.c:313 #, c-format msgid "failed to remove: %s" msgstr "ei onnistunut poistamaan: %s" #: ../src/commands/theme.c:333 msgid "no such theme in the database" msgstr "tietokannassa ei ole tällaista teemaa" #: ../src/commands/theme.c:353 msgid "theme installed (use 'set' to activate it)" msgstr "teema asennettu (aktivoi se käyttämällä 'set')" #: ../src/commands/theme.c:356 msgid "failed to install the theme :-(" msgstr "teeman asennus epäonnistui :-(" #: ../src/commands/theme.c:404 msgid "non-existent" msgstr "olematon" #: ../src/commands/theme.c:417 msgid "theme activated" msgstr "teema aktivoitu" #: ../src/events/account.cpp:56 #, c-format msgid "%s%s%c %s%s@%s%s has logged into a new account %s%s%c" msgstr "%s%s%c %s%s@%s%s on kirjautunut uudelle tilille %s%s%c" #: ../src/events/account.cpp:69 #, c-format msgid "%s%s%c %s%s@%s%s has logged out of their account" msgstr "%s%s%c %s%s@%s%s on kirjautunut ulos tilistään" #: ../src/events/away.cpp:55 #, c-format msgid "%s%s%c %s%s@%s%s is no longer marked as being away!" msgstr "%s%s%c %s%s@%s%s ei ole enää merkitty poissa olevaksi!" #: ../src/events/away.cpp:68 #, c-format msgid "%s%s%c %s%s@%s%s has been marked as being away (%s)" msgstr "%s%s%c %s%s@%s%s on merkitty poissa olevaksi (%s)" #: ../src/events/channel.cpp:100 #, c-format msgid "Homepage for %s%s%s%c%s: %s" msgstr "Kotisivu %s%s%s%c%s: %s" #: ../src/events/channel.cpp:223 #, c-format msgid "%s%s%c %s%s@%s%s has joined %s%s%c" msgstr "%s%s%c %s%s@%s%s on liittynyt %s%s%c" #: ../src/events/channel.cpp:308 #, c-format msgid "%s was kicked from %s%s%c by %s%s%c %s%s%s" msgstr "%s potkaistiin kohteesta %s%s%c / %s%s%c %s%s%s" #. #. * User mode #. #: ../src/events/channel.cpp:580 #, c-format msgid "Mode change %s%s%s for user %c%s%c" msgstr "Tilamuutos %s%s%s käyttäjälle %c%s%c" #: ../src/events/channel.cpp:588 #, c-format msgid "mode/%s%s%s%c%s %s%s%s by %s%s%c" msgstr "tila/%s%s%s%c%s %s%s%s / %s%s%c" #: ../src/events/channel.cpp:703 #, c-format msgid "%s%s%c is now known as %s %s%s%c" msgstr "%s%s%c tunnetaan nykyään nimellä %s %s%s%c" #: ../src/events/channel.cpp:785 #, c-format msgid "%s%s%c %s%s@%s%s has left %s%s%c %s%s%s" msgstr "%s%s%c %s%s@%s%s on lähtenyt %s%s%c %s%s%s" #: ../src/events/channel.cpp:916 #, c-format msgid "%s%s%c %s%s@%s%s has quit %s%s%s" msgstr "%s%s%c %s%s@%s%s on lopettanut %s%s%s" #: ../src/events/channel.cpp:1011 #, c-format msgid "Topic for %s%s%s%c%s: %s" msgstr "Aihe: %s%s%s%c%s: %s" #: ../src/events/channel.cpp:1071 #, c-format msgid "%c%s%c changed the topic of %c%s%c to: %s" msgstr "%c%s%c muutti kohteen %c%s%c aiheen: %s" #: ../src/events/channel.cpp:1149 #, c-format msgid "Topic set by %c%s%c %s%s@%s%s %s%s%s" msgstr "Aiheen on asettanut %c%s%c %s%s@%s%s %s%s%s" #: ../src/events/channel.cpp:1155 #, c-format msgid "Topic set by %c%s%c %s%s%s" msgstr "Aiheen on asettanut %c%s%c %s%s%s" #: ../src/events/chghost.cpp:61 #, c-format msgid "%s%s%s has changed their hostname to %s%s@%s%s" msgstr "%s%s%s on vaihtanut isäntänimensä nimeksi %s%s@%s%s" #: ../src/events/invite.cpp:56 msgid "The invitation has been passed onto the end user" msgstr "Kutsu on välitetty loppukäyttäjälle" #: ../src/events/invite.cpp:103 #, c-format msgid "%c%s%c %s%s@%s%s invites you to %c%s%c" msgstr "%c%s%c %s%s@%s%s kutsuu sinut paikkaan %c%s%c" #. #. * TODO: Write to the channels where the user #. * that's doing the invite is in #. #: ../src/events/invite.cpp:113 #, c-format msgid "%c%s%c %s%s@%s%s invites %c%s%c to %c%s%c" msgstr "%c%s%c %s%s@%s%s kutsuu %c%s%c osoitteeseen %c%s%c" #: ../src/events/misc.cpp:246 #, c-format msgid "Channel %s%s%s%c%s created %s" msgstr "Kanava %s%s%s%c%s luotiin %s" #: ../src/events/misc.cpp:303 #, c-format msgid "mode/%s%s%s%c%s %s%s%s" msgstr "tila/%s%s%s%c%s %s%s%s" #: ../src/events/misc.cpp:354 #, c-format msgid "Channel forwarding from %c%s%c to %c%s%c" msgstr "Kanavan edelleenlähetys paikasta %c%s%c paikkaan %c%s%c" #: ../src/events/misc.cpp:431 #, c-format msgid "Nickname %c%s%c is already in use" msgstr "Lempinimi %c%s%c on jo käytössä" #: ../src/events/misc.cpp:439 msgid "Alternative nickname already tested. Disconnecting..." msgstr "Vaihtoehtoinen lempinimi on jo testattu. Yhteyttä katkaistaan..." #: ../src/events/misc.cpp:444 #, c-format msgid "Attempting to use alt_nick (%s) instead..." msgstr "Yritetään käyttää alt_nick (%s) sen sijaan..." #: ../src/events/misc.cpp:452 msgid "Disconnecting..." msgstr "Yhteyttä katkaistaan..." #: ../src/events/misc.cpp:513 msgid "You're now an IRC operator!" msgstr "Olet nyt IRC operaattori!" #: ../src/include/commandhelp.h:44 msgid "usage: /admin [target]" msgstr "käyttö: /admin [kohde]" #: ../src/include/commandhelp.h:46 msgid "" "The admin command is used to find information about the administrator\n" "of the given server, or current server if 'target' parameter is omit-\n" "ted." msgstr "" "Admin-komentoa käytetään etsimään tietoja tietyn palvelimen\n" "järjestelmänvalvojasta tai nykyisestä palvelimesta, jos\n" "'target'-parametri jätetään pois." #: ../src/include/commandhelp.h:53 ../src/include/commandhelp.h:201 #: ../src/include/commandhelp.h:282 msgid "usage:" msgstr "käyttö:" #: ../src/include/commandhelp.h:60 msgid "Send announcements on IRC." msgstr "Lähetä ilmoituksia IRC:ssä." #: ../src/include/commandhelp.h:65 msgid "usage: /away [reason]" msgstr "käyttö: /away [syy]" #: ../src/include/commandhelp.h:67 msgid "" "Marks yourself as being away (with a reason). If the reason is omitted\n" "you will be marked as no longer being away." msgstr "" "Merkitsee itsesi poissa (syyn kanssa). Jos syy jätetään pois, sinut\n" "merkitään et ole enää poissa." #: ../src/include/commandhelp.h:73 msgid "usage: /ban " msgstr "käyttö: /ban " #: ../src/include/commandhelp.h:75 msgid "" "Sets a channel ban which rejects all users whose 'nick!user@host'\n" "matches the provided mask from joining the channel. Wildcards are okay\n" "and the active window must be an IRC channel." msgstr "" "Asettaa kanavakiellon, joka estää kaikkia käyttäjiä, joiden\n" "'nick!user@host' vastaa annettua maskia, liittymästä\n" "kanavaan. Jokerimerkit ovat kunnossa ja aktiivisen ikkunan on oltava\n" "IRC kanava." #: ../src/include/commandhelp.h:82 msgid "usage: /banlist [channel]" msgstr "käyttö: /banlist [kanava]" #: ../src/include/commandhelp.h:84 msgid "" "Outputs a channel's banlist. If the channel argument is left empty and\n" "the active window is an IRC channel, Swirc will output the banlist for\n" "that channel." msgstr "" "Näyttää kanavan banlistin. Jos kanavaargumentti jätetään tyhjäksi ja\n" "aktiivinen ikkuna on IRC kanava, Swirc tulostaa kyseisen kanavan\n" "banlistin." #: ../src/include/commandhelp.h:91 msgid "usage: /beep " msgstr "käyttö: /beep " #: ../src/include/commandhelp.h:93 msgid "Send beeps. (ICB only)" msgstr "Lähetä piippaukset. (Vain ICB)" #: ../src/include/commandhelp.h:98 msgid "usage: /boot " msgstr "käyttö: /boot " #: ../src/include/commandhelp.h:100 msgid "Kick a user out of your group. (ICB only)" msgstr "Potkaise käyttäjä ryhmästäsi. (Vain ICB)" #: ../src/include/commandhelp.h:105 msgid "usage: /chanserv <[service hostname | --]> [...]" msgstr "käyttö: /chanserv <[palvelun isäntänimi | --]> [...]" #: ../src/include/commandhelp.h:107 msgid "" "Communicate with your IRC network's channel service. If the initial\n" "argument equals to '--', then the value of setting 'chanserv_host' is\n" "used as a service hostname." msgstr "" "Kommunikoi IRC verkkosi kanavapalvelun kanssa. Jos aloitusargumentti\n" "on '--', palvelun isäntänimenä käytetään asetuksen 'chanserv_host'\n" "arvoa." #: ../src/include/commandhelp.h:118 msgid "usage: /ctcp " msgstr "käyttö: /ctcp " #: ../src/include/commandhelp.h:120 msgid "" "Send a CTCP query to 'target', which is either a nickname or an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:123 msgid "Query can be either:" msgstr "" #: ../src/include/commandhelp.h:131 msgid "usage: /cap [ls | list]" msgstr "käyttö: /cap [ls | list]" #: ../src/include/commandhelp.h:133 msgid "" "Lists the (IRCv3) capabilities supported by the server and/or the\n" "capabilities associated with the active connection." msgstr "" "Luetteloi palvelimen tukemat IRCv3 ominaisuudet ja/tai aktiiviseen\n" "yhteyteen liittyvät ominaisuudet." #: ../src/include/commandhelp.h:139 msgid "usage: /cleartoasts" msgstr "käyttö: /cleartoasts" #: ../src/include/commandhelp.h:141 msgid "" "On Windows Swirc sends toast notifications. By running this command\n" "all notifications associated with Swirc will be cleared." msgstr "" "Windowsissa Swirc lähettää toast-ilmoituksia. Suorittamalla tämän\n" "komennon kaikki Swirciin liittyvät ilmoitukset tyhjennetään." #: ../src/include/commandhelp.h:147 msgid "usage: /close" msgstr "käyttö: /close" #: ../src/include/commandhelp.h:149 msgid "" "Closes the active window. It's not possible to close the status\n" "window. And while connected it's not possible to close a channel, in\n" "that case instead use '/part'." msgstr "" "Sulkee aktiivisen ikkunan. Tilaikkunaa ei voi sulkea. Ja kun yhteys on\n" "kytketty, kanavaa ei voi sulkea, vaan käytä siinä tapauksessa '/part'-\n" "komentoa." #: ../src/include/commandhelp.h:156 msgid "usage: /colormap" msgstr "käyttö: /colormap" #: ../src/include/commandhelp.h:158 msgid "Outputs information about colors." msgstr "Tulostaa tietoa väreistä." #: ../src/include/commandhelp.h:163 msgid "usage: /connect [-tls] " msgstr "käyttö: /connect [-tls] " #: ../src/include/commandhelp.h:165 msgid "Connect to given server." msgstr "Yhdistä annettuun palvelimeen." #: ../src/include/commandhelp.h:167 msgid "" "If the port is omitted port 6667 will be chosen. And if the port is\n" "7326 ICB mode is turned on automatically. Further, if the port is 6697\n" "Swirc attempts to initiate a TLS/SSL connection, as well as if '-tls'\n" "is entered." msgstr "" "Jos portti jätetään pois, valitaan portti 6667. Ja jos portti on 7326,\n" "ICB tila kytkeytyy automaattisesti päälle. Lisäksi, jos portti on\n" "6697, Swirc yrittää aloittaa TLS/SSL-yhteyden sekä jos syötetään\n" "'-tls'." #: ../src/include/commandhelp.h:172 msgid "" "It is possible to connect to a certain IRC network by only entering\n" "the network name. For example: '/connect -tls libera', will connect to\n" "Libera Chat using an encrypted connection. Preprogrammed network names\n" "are:" msgstr "" "On mahdollista muodostaa yhteys tiettyyn IRC verkkoon syöttämällä vain\n" "verkon nimi. Esimerkiksi: '/connect -tls libera' muodostaa yhteyden\n" "Libera Chatiin salatulla yhteydellä. Esiohjelmoidut verkkonimet ovat:" #: ../src/include/commandhelp.h:192 msgid "usage: /cycle [channel]" msgstr "käyttö: /cycle [kanava]" #: ../src/include/commandhelp.h:194 msgid "" "Cycle a channel, i.e. '/part' plus '/join'. If the channel argument is\n" "omitted and the active window is an IRC channel, the client will cycle\n" "that channel." msgstr "" "Kierrä kanavaa, eli '/part' ja '/join'. Jos kanava-argumentti jätetään\n" "pois ja aktiivinen ikkuna on IRC kanava, asiakas jaksottaa kyseistä\n" "kanavaa." #: ../src/include/commandhelp.h:209 msgid "" "Get and send files. Swirc implements its own variant of DCC meaning\n" "it's incompatible with other IRC clients. Transport Layer Security is\n" "forced and for now the DCC feature isn't available in ICB mode." msgstr "" "Hanki ja lähetä tiedostoja. Swirc toteuttaa oman DCC versionsa, mikä\n" "tarkoittaa, että se ei ole yhteensopiva muiden IRC asiakkaiden\n" "kanssa. Transport Layer Security on pakotettu, eikä DCC ominaisuus ole\n" "toistaiseksi käytettävissä ICB tilassa." #: ../src/include/commandhelp.h:216 msgid "usage: /deop " msgstr "käyttö: /deop " #: ../src/include/commandhelp.h:218 msgid "Remove the channel operator privilege from another user." msgstr "Poista kanavaoperaattorin oikeudet toiselta käyttäjältä." #: ../src/include/commandhelp.h:223 msgid "usage: /devoice " msgstr "käyttö: /devoice " #: ../src/include/commandhelp.h:225 msgid "Remove the channel voice privilege from another user." msgstr "Poista kanavan puheoikeudet toiselta käyttäjältä." #: ../src/include/commandhelp.h:230 msgid "usage: /die [--I-am-sure]" msgstr "käyttö: /die [--I-am-sure]" #: ../src/include/commandhelp.h:232 msgid "" "An IRC operator can use this command to shutdown the server. Please\n" "confirm that this is what you really want by typing '--I-am-sure'." msgstr "" "IRC operaattori voi käyttää tätä komentoa sammuttaakseen\n" "palvelimen. Vahvista, että tämä on se, mitä todella haluat\n" "kirjoittamalla '--I-am-sure'." #: ../src/include/commandhelp.h:238 msgid "usage: /disconnect [message]" msgstr "käyttö: /disconnect [viesti]" #: ../src/include/commandhelp.h:240 msgid "" "Disconnect from IRC, but don't quit the program. A disconnect message\n" "is optional." msgstr "" "Katkaise yhteys IRC:hen, mutta älä lopeta ohjelmaa. Yhteyden\n" "katkaisuviesti on valinnainen." #: ../src/include/commandhelp.h:246 msgid "usage: /echo " msgstr "käyttö: /echo " #: ../src/include/commandhelp.h:248 msgid "Writes text to the current window without sending anything." msgstr "Kirjoittaa tekstiä nykyiseen ikkunaan lähettämättä mitään." #: ../src/include/commandhelp.h:252 msgid "Echo 'Hello World':" msgstr "Echo 'Hei Maailma':" #: ../src/include/commandhelp.h:253 msgid " /echo Hello World" msgstr " /echo Hei Maailma" #: ../src/include/commandhelp.h:258 msgid "usage: /exlist [channel]" msgstr "käyttö: /exlist [kanava]" #: ../src/include/commandhelp.h:260 msgid "" "Outputs a channel's exception list. An exception mask (+e) overrides a\n" "ban mask. If the channel argument is omitted and the active window is\n" "an IRC channel, the client will output the exception list for that\n" "channel." msgstr "" "Näyttää kanavan poikkeusluettelon. Poikkeusmaski (+e) ohittaa eston\n" "maskin. Jos kanavaargumentti jätetään pois ja aktiivinen ikkuna on IRC\n" "kanava, asiakas tulostaa kyseisen kanavan poikkeusluettelon." #: ../src/include/commandhelp.h:268 msgid "usage: /fetchdic [name]" msgstr "käyttö: /fetchdic [nimi]" #: ../src/include/commandhelp.h:270 msgid "" "Fetches spelling dictionaries. If the name argument is omitted Swirc\n" "will output a list of available dictionaries. The list is obtained\n" "remotely which means its contents can be updated at any time." msgstr "" "Lataa oikeinkirjoitussanakirjoja. Jos nimiargumentti jätetään pois,\n" "Swirc tulostaa luettelon käytettävissä olevista sanakirjoista.\n" "Luettelo hankitaan etänä, joten sen sisältöä voidaan päivittää\n" "milloin tahansa." #: ../src/include/commandhelp.h:276 msgid "Fetch the american english dictionary:" msgstr "Lataa amerikkalaisen englannin sanakirja:" #: ../src/include/commandhelp.h:295 msgid "" "Retrieve and store files on a FTP server. (The communication is done\n" "in clear text, i.e. unencrypted.)" msgstr "" #: ../src/include/commandhelp.h:301 msgid "usage: /gline [ :]" msgstr "käyttö: /gline [ :]" #: ../src/include/commandhelp.h:303 msgid "Network-wide bans." msgstr "Verkon laajuiset kiellot." #: ../src/include/commandhelp.h:305 msgid "" "When a client matches a G-line it cannot connect to ANY server on the\n" "IRC network for 'duration' seconds. If the duration is zero then the\n" "G-line will be permanent." msgstr "" #: ../src/include/commandhelp.h:309 msgid "(If no duration and no reason is given the G-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:318 msgid "usage: /group " msgstr "käyttö: /group " #: ../src/include/commandhelp.h:320 msgid "Changes ICB group." msgstr "Muuttaa ICB ryhmää." #: ../src/include/commandhelp.h:325 msgid "usage: /help [command]" msgstr "käyttö: /help [komento]" #: ../src/include/commandhelp.h:327 msgid "Outputs a list of all available commands." msgstr "Tulostaa luettelon kaikista käytettävissä olevista komennoista." #: ../src/include/commandhelp.h:328 msgid "(Or help for a specific command.)" msgstr "(Tai ohje tietyn komennon kohdalla.)" #: ../src/include/commandhelp.h:333 msgid "usage: /ignore [regex]" msgstr "käyttö: /ignore [regex]" #: ../src/include/commandhelp.h:335 msgid "" "Ignores all 'nick!user@host' that matches the given regular\n" "expression, this by using the POSIX basic regular expression\n" "grammar. This command isn't to be used by beginners and I advice you\n" "to be careful when using it. I highly recommend the use of:" msgstr "" "Ohittaa kaikki 'nick!user@host', jotka vastaavat annettua säännöllistä\n" "lauseketta, käyttämällä POSIXin perussäännöllisten lausekkeiden\n" "kielioppia. Aloittelijat eivät saa käyttää tätä komentoa, ja kehotan\n" "sinua olemaan varovainen sen käytössä. Suosittelen lämpimästi käyttöä:" #: ../src/include/commandhelp.h:340 msgid " 1. ^ Matches the starting position within the string, if it is" msgstr " 1. ^ Vastaa aloituskohtaa merkkijonossa, jos se on säännöllisen" #: ../src/include/commandhelp.h:341 msgid " the first character of the regular expression." msgstr " lausekkeen ensimmäinen merkki." #: ../src/include/commandhelp.h:342 msgid " 2. $ Matches the ending position of the string, if it is the last" msgstr " 2. $ Vastaa merkkijonon loppukohtaa, jos se on säännöllisen" #: ../src/include/commandhelp.h:343 msgid " character of the regular expression." msgstr " lausekkeen viimeinen merkki." #: ../src/include/commandhelp.h:347 msgid "Ignore nickname 'troll':" msgstr "Ohita lempinimi 'troll':" #: ../src/include/commandhelp.h:350 msgid "Ignore all users with username 'troll':" msgstr "Ohita kaikki käyttäjät, joiden käyttäjänimi on 'troll':" #: ../src/include/commandhelp.h:353 msgid "Ignore all users with hostname 'insecure.org':" msgstr "Ohita kaikki käyttäjät, joiden isäntänimi on 'insecure.org':" #: ../src/include/commandhelp.h:356 msgid "Ignore all users with a Chinese domain (.cn):" msgstr "Ohita kaikki käyttäjät, joilla on kiinalainen verkkotunnus (.cn):" #: ../src/include/commandhelp.h:362 msgid "usage: /ilist [channel]" msgstr "käyttö: /ilist [kanava]" #: ../src/include/commandhelp.h:364 msgid "" "Outputs a channel's invitation list. An invitation mask (+I) overrides\n" "the invite-only flag (+i). If the channel argument is omitted and the\n" "active window is an IRC channel, the client will output the invitation\n" "list for that channel." msgstr "" #: ../src/include/commandhelp.h:372 msgid "usage: /info [target]" msgstr "" #: ../src/include/commandhelp.h:374 msgid "The info command returns information about the server." msgstr "" #: ../src/include/commandhelp.h:379 msgid "usage: /invite " msgstr "" #: ../src/include/commandhelp.h:381 msgid "Invites 'targ_nick' to a channel." msgstr "" #: ../src/include/commandhelp.h:386 msgid "usage: /ison [nick2] [nick3] [...]" msgstr "" #: ../src/include/commandhelp.h:388 msgid "Checks whether users are on IRC." msgstr "" #: ../src/include/commandhelp.h:393 msgid "usage: /join [key]" msgstr "" #: ../src/include/commandhelp.h:395 msgid "Joins a channel (optionally by using a key)." msgstr "" #: ../src/include/commandhelp.h:399 msgid "Join a channel with name 'libera':" msgstr "" #: ../src/include/commandhelp.h:402 msgid "Join a key-protected channel:" msgstr "" #: ../src/include/commandhelp.h:408 msgid "usage: /kick [reason]" msgstr "" #: ../src/include/commandhelp.h:410 msgid "" "Kicks one or more users out of a channel. The users are given in a\n" "comma-separated list. A reason is optional and the active window must\n" "be an IRC channel." msgstr "" #: ../src/include/commandhelp.h:417 msgid "usage: /kickban [reason]" msgstr "" #: ../src/include/commandhelp.h:419 msgid "" "Set a channel ban with given 'mask' and kick the user 'nick' out of a\n" "channel. A reason is optional and the active window must be an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:426 msgid "usage: /kill " msgstr "" #: ../src/include/commandhelp.h:428 msgid "Disconnect a user from the connected network." msgstr "" #: ../src/include/commandhelp.h:429 msgid "(Requires IRC op privilege.)" msgstr "" #: ../src/include/commandhelp.h:434 msgid "usage: /kline [ :]" msgstr "" #: ../src/include/commandhelp.h:436 msgid "Server-local bans." msgstr "" #: ../src/include/commandhelp.h:438 msgid "" "When a client matches a K-line it cannot connect to the local server\n" "for 'duration' seconds. If the duration is zero then the K-line will\n" "be permanent." msgstr "" #: ../src/include/commandhelp.h:442 msgid "(If no duration and no reason is given the K-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:451 msgid "usage: /list [min_users][,pattern][...]]" msgstr "" #: ../src/include/commandhelp.h:453 msgid "" "Lists channels and their topics. Without any arguments the list is\n" "HUGE. For example, '/list >1500' will only list channels that have\n" "more than 1500 users." msgstr "" #: ../src/include/commandhelp.h:457 msgid "" "Depending on the IRC server software used by your network the usage\n" "may differ." msgstr "" #: ../src/include/commandhelp.h:463 msgid "usage: /me " msgstr "" #: ../src/include/commandhelp.h:465 msgid "Send an 'action' message. (Used to simulate role playing on IRC.)" msgstr "" #: ../src/include/commandhelp.h:470 msgid "usage: /mode [...]" msgstr "" #: ../src/include/commandhelp.h:472 msgid "Alter modes." msgstr "" #: ../src/include/commandhelp.h:476 msgid " o - give/take the channel operator privilege" msgstr "" #: ../src/include/commandhelp.h:477 msgid " v - give/take the channel voice privilege" msgstr "" #: ../src/include/commandhelp.h:479 msgid " i - invite-only channel" msgstr "" #: ../src/include/commandhelp.h:480 msgid " m - moderated channel" msgstr "" #: ../src/include/commandhelp.h:481 msgid " n - no messages to a channel from clients on the outside" msgstr "" #: ../src/include/commandhelp.h:482 msgid " p - private channel" msgstr "" #: ../src/include/commandhelp.h:483 msgid " s - secret channel" msgstr "" #: ../src/include/commandhelp.h:484 msgid " t - topic settable by channel operators only" msgstr "" #: ../src/include/commandhelp.h:486 msgid " k - set/remove the channel key (password)" msgstr "" #: ../src/include/commandhelp.h:487 msgid " l - set/remove the channel user limit" msgstr "" #: ../src/include/commandhelp.h:489 msgid " b - set/remove a ban mask" msgstr "" #: ../src/include/commandhelp.h:490 msgid " e - set/remove an exception mask to override a ban mask" msgstr "" #: ../src/include/commandhelp.h:491 msgid " I - set/remove an invitation mask to override the invite-only flag" msgstr "" #: ../src/include/commandhelp.h:495 msgid " i - marks a user as invisible" msgstr "" #: ../src/include/commandhelp.h:496 msgid " w - the user receives wallops messages" msgstr "" #: ../src/include/commandhelp.h:500 msgid " Give channel operator privilege to 'Companion' in #foo:" msgstr "" #: ../src/include/commandhelp.h:503 msgid " Restrict messaging to channel #linux:" msgstr "" #: ../src/include/commandhelp.h:506 msgid " Limit user count for #freenode to 10:" msgstr "" #: ../src/include/commandhelp.h:509 msgid " Deny all users with hostname spammers.net from joining #chatzone:" msgstr "" #: ../src/include/commandhelp.h:512 msgid " Turn on reception of WALLOPS messages:" msgstr "" #: ../src/include/commandhelp.h:518 msgid "usage: /msg " msgstr "" #: ../src/include/commandhelp.h:520 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels." msgstr "" #: ../src/include/commandhelp.h:526 msgid "usage: /nick " msgstr "" #: ../src/include/commandhelp.h:528 msgid "Sets your nickname." msgstr "" #: ../src/include/commandhelp.h:533 msgid "usage: /nickserv <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:535 msgid "Communicate with your IRC network's nickname service." msgstr "" #: ../src/include/commandhelp.h:537 msgid "If the initial argument equals to '--' then the:" msgstr "" #: ../src/include/commandhelp.h:539 msgid " 1) Value of setting 'nickserv_host' will be used as service" msgstr "" #: ../src/include/commandhelp.h:540 msgid " hostname." msgstr "" #: ../src/include/commandhelp.h:541 msgid " 2) Command call won't be added to the command history provided" msgstr "" #: ../src/include/commandhelp.h:542 msgid " that the second argument is 'identify'." msgstr "" #: ../src/include/commandhelp.h:544 msgid "" "The correct service hostname is not always the same as the visible\n" "hostname of NickServ. FYI at the AnonOps IRC network the visible\n" "hostname of NickServ is anonops.in (when this text was written)\n" "but you should use 'services.anonops.com'. As a fallback:" msgstr "" #: ../src/include/commandhelp.h:548 msgid "" "'/query NickServ' can be used in order to communicate with the\n" "service." msgstr "" #: ../src/include/commandhelp.h:554 msgid "usage: /notice " msgstr "" #: ../src/include/commandhelp.h:556 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels. (In notice form.)" msgstr "" #: ../src/include/commandhelp.h:562 msgid "usage: /op " msgstr "" #: ../src/include/commandhelp.h:564 msgid "Gives the channel operator privilege to another user." msgstr "" #: ../src/include/commandhelp.h:569 msgid "usage: /oper " msgstr "" #: ../src/include/commandhelp.h:571 msgid "Identifies yourself as an IRC operator." msgstr "" #: ../src/include/commandhelp.h:576 msgid "usage: /part [channel] [message]" msgstr "" #: ../src/include/commandhelp.h:578 msgid "" "Leaves a channel (optionally with a message). If the command is called\n" "without any arguments and the current window is an IRC channel, that\n" "channel will be the target." msgstr "" #: ../src/include/commandhelp.h:584 msgid " Leave channel #chatzone with message 'bye':" msgstr "" #: ../src/include/commandhelp.h:585 msgid " /part #chatzone bye" msgstr "" #: ../src/include/commandhelp.h:590 msgid "usage: /passmod " msgstr "" #: ../src/include/commandhelp.h:592 msgid "Pass the ICB moderation privilege to another group member." msgstr "" #: ../src/include/commandhelp.h:597 msgid "usage: /qbot <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:599 msgid "" "Communicate with QuakeNet's network service, the Q bot. If the initial\n" "argument equals to '--', then the value of setting 'qbot_host' is used\n" "as a service hostname." msgstr "" #: ../src/include/commandhelp.h:606 msgid "usage: /query [nick]" msgstr "" #: ../src/include/commandhelp.h:608 msgid "" "Starts a private conversation with 'nick'. If 'nick' is omitted and\n" "the active window is a private conversation, the action will be to\n" "close it." msgstr "" #: ../src/include/commandhelp.h:615 msgid "usage: /quit [message]" msgstr "" #: ../src/include/commandhelp.h:617 msgid "" "Disconnect from IRC and quit the program. A disconnect message is\n" "optional." msgstr "" #: ../src/include/commandhelp.h:623 msgid "usage: /rehash" msgstr "" #: ../src/include/commandhelp.h:625 msgid "" "The rehash command is an administrative command which can be used by\n" "an IRC operator to force the server to re-read and process its conf-\n" "iguration file." msgstr "" #: ../src/include/commandhelp.h:632 msgid "usage: /resize" msgstr "" #: ../src/include/commandhelp.h:634 msgid "" "Resize the terminal. For example, Windows doesn't send 'SIGWINCH',\n" "instead this command can be used. First resize the window then run\n" "this command." msgstr "" #: ../src/include/commandhelp.h:641 msgid "usage: /restart [--I-am-sure]" msgstr "" #: ../src/include/commandhelp.h:643 msgid "" "An IRC operator can use the 'restart'-command to force the server to\n" "restart itself." msgstr "" #: ../src/include/commandhelp.h:649 msgid "usage: /rules" msgstr "" #: ../src/include/commandhelp.h:651 msgid "" "Outputs network/server rules. Not all IRC server software supports\n" "this command. (It's actually quite rare.)" msgstr "" #: ../src/include/commandhelp.h:657 msgid "usage: /sasl [...]" msgstr "" #: ../src/include/commandhelp.h:659 msgid "Simple Authentication and Security Layer." msgstr "" #: ../src/include/commandhelp.h:660 msgid "Operation can be either:" msgstr "" #: ../src/include/commandhelp.h:672 msgid "" "SASL is a method that lets you identify with NickServ during the\n" "connection process eliminating the need to do it manually." msgstr "" #: ../src/include/commandhelp.h:674 msgid "(To use SASL you must register your nickname.)" msgstr "" #: ../src/include/commandhelp.h:678 msgid " Identification using mechanism ecdsa-nist256p-challenge:" msgstr "" #: ../src/include/commandhelp.h:686 msgid "" " (The only IRC network that I know of that is supporting this\n" " mechanism is Libera Chat.)" msgstr "" #: ../src/include/commandhelp.h:689 msgid " Identification using mechanism 'plain':" msgstr "" #: ../src/include/commandhelp.h:698 msgid "usage: /say " msgstr "" #: ../src/include/commandhelp.h:700 msgid "" "Say a message. This command can be used if you want say something with\n" "a leading command-character, i.e. a slash." msgstr "" #: ../src/include/commandhelp.h:705 msgid " /say // A single-line comment in C++" msgstr "" #: ../src/include/commandhelp.h:710 msgid "usage: /servlist [ []]" msgstr "" #: ../src/include/commandhelp.h:712 msgid "" "Lists services currently connected to your IRC network. Arguments,\n" "if given, can be used to restrict the output result." msgstr "" #: ../src/include/commandhelp.h:722 msgid "usage: /servstats [ []]" msgstr "" #: ../src/include/commandhelp.h:724 msgid "This command is used to query statistics of a certain IRC server." msgstr "" #: ../src/include/commandhelp.h:729 msgid "usage: /set [[setting] [value]]" msgstr "" #: ../src/include/commandhelp.h:731 msgid "Alter Swirc settings." msgstr "" #: ../src/include/commandhelp.h:736 msgid "" " Bools are case insensitive and can have one of the following\n" " values:" msgstr "" #: ../src/include/commandhelp.h:738 msgid " - on, true or yes" msgstr "" #: ../src/include/commandhelp.h:739 msgid " - off, false or no" msgstr "" #: ../src/include/commandhelp.h:742 msgid "" " Integers. Swirc implements a min/max value for each integer in\n" " order to keep its value safe. The error log will tell if the\n" " restrictions for an integer aren't within limits and, if so, that\n" " a preprogrammed fallback value is being used instead." msgstr "" #: ../src/include/commandhelp.h:748 msgid " An arbitrary sequence of characters" msgstr "" #: ../src/include/commandhelp.h:752 msgid " Output the current values of all settings:" msgstr "" #: ../src/include/commandhelp.h:753 msgid " /set (without any arguments)" msgstr "" #: ../src/include/commandhelp.h:755 msgid " Turn beeps on/off:" msgstr "" #: ../src/include/commandhelp.h:762 msgid "usage: /squery " msgstr "" #: ../src/include/commandhelp.h:764 msgid "" "This command is used similarly to '/msg'. The only difference is that\n" "the recipient MUST be a service." msgstr "" #: ../src/include/commandhelp.h:770 msgid "usage: /stats [channel]" msgstr "" #: ../src/include/commandhelp.h:772 msgid "" "Outputs a channel's user statistics. If the channel argument is left\n" "empty and the active window is an IRC channel, Swirc will output the\n" "user statistics for that channel." msgstr "" #: ../src/include/commandhelp.h:779 msgid "usage: /theme [install | list-remote | set ]" msgstr "" #: ../src/include/commandhelp.h:781 msgid "Management of themes on-the-fly." msgstr "" #: ../src/include/commandhelp.h:785 msgid " Install a theme named 'bx':" msgstr "" #: ../src/include/commandhelp.h:788 msgid " List all available themes:" msgstr "" #: ../src/include/commandhelp.h:791 msgid " Activate an installed theme with name 'bx':" msgstr "" #: ../src/include/commandhelp.h:797 msgid "usage: /time " msgstr "" #: ../src/include/commandhelp.h:799 msgid "" "Send a CTCP TIME request to 'target', which is either a nickname or an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:805 msgid "usage: /topic [new topic]" msgstr "" #: ../src/include/commandhelp.h:807 msgid "" "Sets a new topic for a channel. If 'new topic' is omitted the action\n" "will be to display the current topic. (The active window must be an\n" "IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:814 msgid "usage: /unban " msgstr "" #: ../src/include/commandhelp.h:816 msgid "Removes a channel ban. (The active window must be an IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:826 msgid "usage: /unignore [#]" msgstr "" #: ../src/include/commandhelp.h:828 msgid "Deletes a regular expression from the ignore list." msgstr "" #: ../src/include/commandhelp.h:837 msgid "usage: /version " msgstr "" #: ../src/include/commandhelp.h:839 msgid "" "Send a CTCP VERSION request to 'target', which is either a nickname or\n" "an IRC channel." msgstr "" #: ../src/include/commandhelp.h:845 msgid "usage: /voice " msgstr "" #: ../src/include/commandhelp.h:847 msgid "Gives the channel voice privilege to another user." msgstr "" #: ../src/include/commandhelp.h:852 msgid "usage: /wallops " msgstr "" #: ../src/include/commandhelp.h:854 msgid "" "The 'wallops'-command is used to send a message to all currently\n" "connected users who have set the 'w' user mode for themselves." msgstr "" #: ../src/include/commandhelp.h:860 msgid "usage: /who " msgstr "" #: ../src/include/commandhelp.h:862 msgid "" "Generates a query which returns a list of information which matches\n" "the provided 'mask'." msgstr "" #: ../src/include/commandhelp.h:867 msgid " Show the Libera Chat crew:" msgstr "" #: ../src/include/commandhelp.h:870 msgid " Show users with a German domain name:" msgstr "" #: ../src/include/commandhelp.h:876 msgid "usage: /whois " msgstr "" #: ../src/include/commandhelp.h:878 msgid "Asks after information about another user." msgstr "" #: ../src/include/commandhelp.h:883 msgid "usage: /wholeft" msgstr "" #: ../src/include/commandhelp.h:885 msgid "" "By using this command you can see who left during a network server\n" "split. The command takes no arguments and the active window must be an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:892 msgid "usage: /znc [*module] " msgstr "" #: ../src/include/commandhelp.h:894 msgid "" "Simplifies the communication with ZNC which is a popular\n" "'IRC bouncer'." msgstr "" #: ../src/include/commandhelp.h:899 msgid " Output your ZNC version:" msgstr "" #: ../src/include/commandhelp.h:902 msgid " Identical to the previous example:" msgstr "" #: ../src/interpreter.cpp:56 ../src/interpreter.cpp:87 #, c-format msgid "%s: fatal: string was truncated" msgstr "%s: kohtalokas: merkkijono katkaistiin" #: ../src/io-loop.c:596 #, c-format msgid " Swirc %s by %s" msgstr " Swirc %s, kirjoittanut %s" #: ../src/io-loop.c:597 #, c-format msgid " Compiled on %s%s %s%s" msgstr " Kokoonpantu %s%s %s%s" #: ../src/io-loop.c:603 #, c-format msgid "Current language %s%s%s" msgstr "Nykyinen kieli %s%s%s" #: ../src/io-loop.c:606 #, c-format msgid "Program settings are stored in %s%s%s" msgstr "Ohjelman asetukset tallennetaan kansioon %s%s%s" #: ../src/io-loop.c:608 #, c-format msgid "%c%hd%c color pairs have been initialized" msgstr "%c%hd%c väriparia on alustettu" #: ../src/io-loop.c:610 msgid "Type /help for a list of commands; or /help " msgstr "Kirjoita /help nähdäksesi komentoluettelon; tai /help " #: ../src/io-loop.c:612 msgid "for help of a specific command" msgstr "saadaksesi apua tietyn komennon suorittamiseen" #: ../src/io-loop.c:613 msgid "Type F1 for keys" msgstr "Kirjoita F1 näppäimistön näppäimiä varten" #: ../src/io-loop.c:615 #, c-format msgid "Error log size %s%.1f KB%s" msgstr "Virhelokin koko %s%.1f KB%s" #: ../src/irc.c:332 #, c-format msgid "%s: no nickname" msgstr "%s: ei lempinimeä" #: ../src/irc.c:451 #, c-format msgid "%s: unsupported extension" msgstr "%s: ei tuettu laajennus" #: ../src/irc.c:648 #, c-format msgid "Unknown normal event: %s" msgstr "Tuntematon normaali tapahtuma: %s" #: ../src/irc.c:659 #, c-format msgid "Unknown numeric event: %s" msgstr "Tuntematon numeerinen tapahtuma: %s" #: ../src/irc.c:667 #, c-format msgid "Erroneous event: %s" msgstr "" #: ../src/log.c:217 #, c-format msgid "Logging for window (refnum: %d) now off" msgstr "Ikkunan kirjaaminen (refnum: %d) on nyt pois päältä" #: ../src/log.c:221 #, c-format msgid "Logging for window (refnum: %d) now on" msgstr "Ikkunan kirjaaminen (refnum: %d) on nyt päällä" #: ../src/main.cpp:84 #, c-format msgid "A duplicate of option -%c found" msgstr "Vaihtoehdosta -%c löytyi kopio" #: ../src/main.cpp:144 msgid "" "\n" "Options:\n" "\n" msgstr "" "\n" "Vaihtoehdot:\n" "\n" #: ../src/main.cpp:145 msgid " -4 Use IPv4 addresses only\n" msgstr " -4 Käytä IPv4 osoitteita vain\n" #: ../src/main.cpp:146 msgid " -6 Use IPv6 addresses only\n" msgstr " -6 Käytä IPv6 osoitteita vain\n" #: ../src/main.cpp:147 msgid " -?, --help Output help\n" msgstr " -?, --help Lähtö help\n" #: ../src/main.cpp:148 msgid " -C Do not change color definitions\n" msgstr " -C Älä muuta värimääritysten\n" #: ../src/main.cpp:149 msgid " -P Permanently disable SASL authentication\n" msgstr " -P Poista SASL todennus pysyvästi käytöstä\n" #: ../src/main.cpp:150 msgid " -R Disable TLS/SSL peer verification\n" msgstr " -R Poista käytöstä TLS/SSL vertaisvahvistus\n" #: ../src/main.cpp:151 msgid " -S Force TLS\n" msgstr " -S Pakota TLS\n" #: ../src/main.cpp:156 msgid " -W Equal effect as flag 'p' but non-interactive\n" msgstr "" " -W Sama vaikutus kuin lipulla 'p', mutta ei " "interaktiivinen\n" #: ../src/main.cpp:157 msgid " -X Disable all IRCv3 extensions\n" msgstr " -X Poista kaikki IRCv3 laajennukset käytöstä\n" #: ../src/main.cpp:158 msgid " -c Connect to IRC server\n" msgstr " -c Muodosta yhteys IRC palvelimeen\n" #: ../src/main.cpp:159 msgid " -d Debug logging\n" msgstr " -d Kirjaa virheenkorjausviestejä\n" #: ../src/main.cpp:160 msgid " -i Turn on Internet Citizen's Band mode\n" msgstr " -i Ota Internet Citizen's Band tila käyttöön\n" #: ../src/main.cpp:161 msgid " -j A comma-separated list of channels to join\n" msgstr " -j Pilkuilla erotettu luettelo kanavista\n" #: ../src/main.cpp:162 msgid " -n Online nickname\n" msgstr " -n Online nickname\n" #: ../src/main.cpp:163 msgid "" " -p Query for server password (for private servers)\n" msgstr "" " -p Kyselyn palvelimen salasanaa (yksityisten " "palvelimien)\n" #: ../src/main.cpp:164 msgid " -r Your real name\n" msgstr " -r Oikea nimesi\n" #: ../src/main.cpp:165 msgid " -u Your username\n" msgstr " -u Sinun käyttäjä nimesi\n" #: ../src/main.cpp:166 msgid " -v, --version Output Swirc version\n" msgstr " -v, --version Lähtö Swirc versio\n" #: ../src/main.cpp:167 msgid " -x Config file\n" msgstr " -x Config tiedosto\n" #: ../src/main.cpp:230 msgid "Unhandled exception!" msgstr "Käsittelemätön poikkeus!" #: ../src/main.cpp:290 #, c-format msgid "Usage: %s [OPTION] ...\n" msgstr "Käyttö: %s [VAIHTOEHTO] ...\n" #: ../src/main.cpp:380 msgid "forbidden channel name characters" msgstr "" #: ../src/main.cpp:532 #, c-format msgid "%s: -%c: unrecognized option" msgstr "%s: -%c: tunnistamaton vaihtoehto" #: ../src/main.cpp:537 #, c-format msgid "%s: -%c: option argument missing" msgstr "%s: -%c: vaihtoehdon argumentti puuttuu" #: ../src/main.cpp:706 msgid "fatal: failed to initialize signal handling" msgstr "" #: ../src/main.cpp:728 msgid "fatal: running the program with root privileges is forbidden" msgstr "fataali: ohjelman suorittaminen pääkäyttäjän oikeuksilla on kielletty" #: ../src/main.cpp:750 msgid "Initialization of the Curses library not possible" msgstr "" #: ../src/messagetags.c:150 #, c-format msgid "%s: server time error" msgstr "%s: palvelinaikavirhe" #: ../src/nestHome.c:143 #, c-format msgid "%s exists. However; it isn't a directory." msgstr "%s on olemassa. Kuitenkin; se ei ole hakemisto." #: ../src/nestHome.c:147 ../src/nestHome.c:150 msgid "Cannot make directory" msgstr "Hakemistoa ei voi tehdä" #: ../src/nestHome.c:177 msgid "Please decrypt your SASL password..." msgstr "Ole hyvä ja pura SASL salasanasi salaus..." #: ../src/nestHome.c:178 msgid "(One attempt)" msgstr "(Yksi yritys)" #: ../src/nestHome.c:186 msgid "Password: " msgstr "Salasana: " #: ../src/nestHome.c:216 msgid "Decryption failed" msgstr "Salauksen purku epäonnistui" #: ../src/nestHome.c:219 msgid "Wrong password" msgstr "Väärä salasana" #: ../src/nestHome.c:230 msgid "The password seems reasonable" msgstr "Salasana vaikuttaa järkevältä" #: ../src/nestHome.c:242 msgid "Press " msgstr "Paina " #: ../src/nestHome.c:267 ../src/nestHome.c:278 ../src/nestHome.c:337 #, c-format msgid "%s exists -- but isn't a regular file." msgstr "%s on olemassa -- mutta se ei ole tavallinen tiedosto." #: ../src/nestHome.c:270 ../src/nestHome.c:345 #, c-format msgid "%s no such file or directory. Exiting..." msgstr "%s ei tällaista tiedostoa tai hakemistoa. Poistuu..." #: ../src/nestHome.c:301 msgid "Warning!" msgstr "Varoitus!" #: ../src/nestHome.c:302 msgid "" "A decrypted SASL password was found in the read configuration file -- " "cannot continue!" msgstr "" "Luetusta määritystiedostosta löytyi salauksesta purettu SASL salasana -- " "ei voi jatkaa!" #: ../src/nestHome.c:333 msgid "The item 'theme' in the user configuration file holds no data. Error." msgstr "" #: ../src/nestHome.c:358 msgid "Can't resolve the home path!" msgstr "" #: ../src/network.cpp:183 msgid "Hostname checking failed!" msgstr "Isäntänimen tarkistus epäonnistui!" #: ../src/network.cpp:185 msgid "Hostname checking OK!" msgstr "Isäntänimen tarkistus OK!" #: ../src/network.cpp:242 msgid "Connected!" msgstr "Kytkettynä!" #: ../src/network.cpp:311 msgid "Get a list of IP addresses completed" msgstr "Hanki luettelo IP osoitteista, valmis" #: ../src/network.cpp:539 #, c-format msgid "Connecting to %s (%s)" msgstr "Yhdistetään kohteeseen %s (%s)" #: ../src/network.cpp:807 msgid "Connection to IRC server lost" msgstr "Yhteys IRC palvelimeen katkesi" #: ../src/network.cpp:818 msgid "Disconnected" msgstr "Yhteys katkaistu" #: ../src/readline.c:623 msgid "--------------- Keys ---------------" msgstr "" #: ../src/readline.c:625 msgid "CTRL+a Move to beginning of line" msgstr "" #: ../src/readline.c:626 msgid "CTRL+e Move to end of line" msgstr "" #: ../src/readline.c:627 msgid "CTRL+b Move cursor backward" msgstr "" #: ../src/readline.c:628 msgid "CTRL+f Move cursor forward" msgstr "" #: ../src/readline.c:629 msgid "CTRL+d Delete" msgstr "" #: ../src/readline.c:630 msgid "CTRL+g Clear readline input" msgstr "" #: ../src/readline.c:631 msgid "CTRL+l Toggle logging on/off" msgstr "" #: ../src/readline.c:632 msgid "CTRL+n Next window" msgstr "" #: ../src/readline.c:633 msgid "CTRL+p Previous window" msgstr "" #: ../src/readline.c:634 msgid "CTRL+w List all windows" msgstr "" #: ../src/readline.c:635 msgid "PG UP Scroll up" msgstr "" #: ../src/readline.c:636 msgid "PG DOWN Scroll down" msgstr "" #: ../src/readline.c:637 msgid "Up arrow History previous" msgstr "" #: ../src/readline.c:638 msgid "Down arrow History next" msgstr "" #: ../src/readline.c:639 msgid "F2 Spell word" msgstr "" #: ../src/readline.c:640 msgid "F3 Scroll nicklist up" msgstr "" #: ../src/readline.c:641 msgid "F4 Scroll nicklist down" msgstr "" #: ../src/readline.c:642 msgid "F11 Close window" msgstr "" #: ../src/readline.c:643 msgid "F12 Close all private conversations" msgstr "" #: ../src/sig-w32.c:26 msgid "Abnormal termination" msgstr "Keskeytetty" #: ../src/sig-w32.c:27 msgid "Floating-point error" msgstr "Liukulukupoikkeus" #: ../src/sig-w32.c:28 msgid "Illegal instruction" msgstr "Virheellinen käsky" #: ../src/sig-w32.c:29 msgid "Illegal storage access" msgstr "Muistialueen ylitys" #: ../src/sig-w32.c:30 msgid "Termination request" msgstr "Päätetty" #: ../src/spell.cpp:137 ../src/spell.cpp:141 #, c-format msgid "%s: %s not found" msgstr "%s: %s ei löydy" #: ../src/spell.cpp:317 msgid "no more suggestions" msgstr "ei enempää ehdotuksia" #: ../src/spell.cpp:344 msgid "suggestions:" msgstr "ehdotuksia:" #: ../src/spell.cpp:372 #, c-format msgid "%ls is correct" msgstr "%ls on oikein" #: ../src/spell.cpp:377 #, c-format msgid "%ls is incorrect" msgstr "%ls on väärä" #: ../src/statusbar.cpp:222 msgid "Log: " msgstr "Loki: " #: ../src/statusbar.cpp:227 msgid "-- MORE --" msgstr "-- LISÄÄ --" #: ../src/tls-server.cpp:205 msgid "Already accepting DCC connections..." msgstr "DCC yhteyksiä jo hyväksytään..." #: ../src/tls-server.cpp:229 #, c-format msgid "Accepting DCC connections at port: %d" msgstr "Hyväksytään DCC yhteyksiä portissa: %d" #: ../src/tls-server.cpp:241 msgid "Out of memory" msgstr "Muisti loppu" #: ../src/tls-server.cpp:250 msgid "Stopped accepting DCC connections" msgstr "DCC yhteyksien hyväksyminen lopetettu" #, c-format #~ msgid "%s: cannot find time" #~ msgstr "%s: ei löydy aikaa" #, c-format #~ msgid "%s: empty message" #~ msgstr "%s: tyhjä viesti" #, c-format #~ msgid "%s: too many semicolons" #~ msgstr "%s: liian monta puolipistettä" swirc-3.5.5/po/fr/000077500000000000000000000000001501213070300136705ustar00rootroot00000000000000swirc-3.5.5/po/fr/swirc.po000066400000000000000000001403601501213070300153630ustar00rootroot00000000000000# French translations for swirc package # Traductions françaises du paquet swirc. # This file is put in the public domain. # , 2021. # msgid "" msgstr "" "Project-Id-Version: swirc 3.5.5\n" "Report-Msgid-Bugs-To: https://github.com/uhlin/swirc/issues\n" "POT-Creation-Date: 2025-05-17 12:31+0200\n" "PO-Revision-Date: 2025-05-17 12:12+0200\n" "Last-Translator: Markus Uhlin \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../src/commands/colormap.cpp:153 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:222 msgid "Yes" msgstr "Oui" #: ../src/commands/colormap.cpp:154 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:223 msgid "No" msgstr "Non" #: ../src/commands/connect.c:211 msgid "Connect using password? [Y/n]: " msgstr "Se connecter en utilisant un mot de passe? [Y/n]: " #: ../src/commands/connect.c:219 ../src/commands/connect.c:280 #: ../src/commands/connect.c:327 ../src/nestHome.c:202 msgid "input too big" msgstr "la saisie utilisateur est trop grande" #: ../src/commands/connect.c:248 #, c-format msgid "%s: string copy error" msgstr "%s: erreur de copie de string" #: ../src/commands/connect.c:264 msgid "Server password (will not echo): " msgstr "Mot de passe du serveur (ne fera pas écho): " #: ../src/commands/connect.c:317 #, c-format msgid "Your choice (0-%d): " msgstr "Votre choix (0-%d): " #: ../src/commands/connect.c:520 #, c-format msgid "Next reconnect attempt in %ld seconds..." msgstr "Prochaine tentative de reconnexion dans %ld secondes..." #: ../src/commands/connect.c:532 msgid "Reconnection aborted!" msgstr "Reconnexion annulée!" #: ../src/commands/connect.c:572 msgid "Cannot initiate use of the Winsock DLL" msgstr "Impossible de lancer l'utilisation de la DLL Winsock" #: ../src/commands/connect.c:576 msgid "Use of the Winsock DLL granted" msgstr "Utilisation de la DLL Winsock accordée" #: ../src/commands/ctcp.c:77 ../src/commands/ftp.cpp:873 #: ../src/commands/ftp.cpp:960 ../src/commands/ftp.cpp:1009 #: ../src/commands/ftp.cpp:1046 ../src/commands/ftp.cpp:1128 msgid "Insufficient arguments" msgstr "Arguments insuffisants" #: ../src/commands/ctcp.c:86 #, c-format msgid "%s: insufficient arguments" msgstr "%s: arguments insuffisants" #: ../src/commands/ctcp.c:89 #, c-format msgid "%s: invalid target" msgstr "%s: cible non valide" #: ../src/commands/ctcp.c:94 #, c-format msgid "%s: forbidden channel name characters" msgstr "%s: caractères de nom de canal interdits" #: ../src/commands/ctcp.c:105 msgid "Invalid CTCP query" msgstr "Requête CTCP non valide" #: ../src/commands/dcc.cpp:212 msgid "Write error" msgstr "Erreur d'écriture de fichier" #: ../src/commands/dcc.cpp:337 #, c-format msgid "%s: wrote: %s" msgstr "%s: a écrit: %s" #: ../src/commands/dcc.cpp:340 #, c-format msgid "%s: did not complete: %s" msgstr "%s: n'a pas terminé: %s" #: ../src/commands/dcc.cpp:1017 #, c-format msgid "%sStarted%s: %s" msgstr "%sDémarré%s: %s" #: ../src/commands/dcc.cpp:1019 #, c-format msgid "%sStopped%s: %s" msgstr "%sArrêté%s: %s" #: ../src/commands/dcc.cpp:1393 #, c-format msgid "sending '%s' to %s (%.1f%c)..." msgstr "envoi de '%s' à %s (%.1f%c)..." #: ../src/commands/dcc.cpp:1484 msgid "Missing certs. Not starting the DCC server. Please create them." msgstr "" "Certificats manquants. Le serveur DCC ne démarre pas. Veuillez les créer." #: ../src/commands/dcc.cpp:1590 #, c-format msgid "%s: added: '%s' (%.1f%c)" msgstr "%s: ajouté: '%s' (%.1f%c)" #: ../src/commands/dcc.cpp:1592 #, c-format msgid "%s: from: %s <%s@%s>" msgstr "%s: de: %s <%s@%s>" #: ../src/commands/dcc.cpp:1594 msgid "To get the file, type:" msgstr "Pour obtenir le fichier, tapez:" #: ../src/commands/dcc.cpp:1845 ../src/commands/ftp.cpp:805 #, c-format msgid "%s: file read error" msgstr "%s: erreur de lecture du fichier" #: ../src/commands/dcc.cpp:1850 #, c-format msgid "%s: tls write error" msgstr "%s: erreur d'écriture tls" #: ../src/commands/dcc.cpp:1867 #, c-format msgid "started: %s" msgstr "commencé: %s" #: ../src/commands/dcc.cpp:1868 #, c-format msgid "stopped: %s" msgstr "arrêté: %s" #: ../src/commands/dcc.cpp:1885 msgid "read request error" msgstr "erreur de demande de lecture" #: ../src/commands/dcc.cpp:1892 msgid "unable to find the send object" msgstr "impossible de trouver l'objet d'envoi" #: ../src/commands/dcc.cpp:1900 msgid "already sent file" msgstr "fichier déjà envoyé" #: ../src/commands/dcc.cpp:1903 msgid "the send object is in a locked state" msgstr "l'objet d'envoi est dans un état verrouillé" #: ../src/commands/dcc.cpp:1908 msgid "file open error" msgstr "erreur d'ouverture du fichier" #: ../src/commands/dcc.cpp:1927 #, c-format msgid "%s: successfully sent file: %s" msgstr "%s: fichier envoyé avec succès: %s" #: ../src/commands/dcc.cpp:1931 #, c-format msgid "%s: file transfer incomplete: %s" msgstr "%s: transfert de fichier incomplet: %s" #: ../src/commands/ftp.cpp:181 msgid "Invalid username" msgstr "Nom d'utilisateur invalide" #: ../src/commands/ftp.cpp:183 msgid "Too long password" msgstr "Mot de passe trop long" #: ../src/commands/ftp.cpp:185 msgid "Invalid hostname" msgstr "Nom d'hôte invalide" #: ../src/commands/ftp.cpp:187 msgid "Invalid port number" msgstr "Numéro de port invalide" #: ../src/commands/ftp.cpp:190 ../src/network.cpp:308 msgid "Unable to get a list of IP addresses" msgstr "Impossible d'obtenir une liste d'adresses IP" #: ../src/commands/ftp.cpp:193 ../src/commands/ftp.cpp:533 #: ../src/network.cpp:175 msgid "Failed to establish a connection" msgstr "Impossible d'établir une connexion" #: ../src/commands/ftp.cpp:204 ../src/commands/ftp.cpp:886 #: ../src/commands/ftp.cpp:1257 msgid "Cannot send" msgstr "Ne peut pas envoyer" #: ../src/commands/ftp.cpp:521 msgid "Failed to create an endpoint for communication" msgstr "Échec de la création d'un point de terminaison pour la communication" #: ../src/commands/ftp.cpp:813 #, c-format msgid "%s: bytes sent mismatch bytes read" msgstr "%s: les octets envoyés ne correspondent pas aux octets lus" #: ../src/commands/ftp.cpp:876 ../src/commands/ftp.cpp:1245 msgid "No control connection" msgstr "Aucune connexion de contrôle" #: ../src/commands/ftp.cpp:879 ../src/commands/ftp.cpp:901 #: ../src/commands/ftp.cpp:1253 msgid "Invalid network socket" msgstr "Socket réseau non valide" #: ../src/commands/ftp.cpp:963 ../src/commands/ftp.cpp:1049 msgid "Blank file path" msgstr "Chemin du fichier vierge" #: ../src/commands/ftp.cpp:1249 msgid "Already in passive mode" msgstr "Déjà en mode passif" #: ../src/commands/ftp.cpp:1272 msgid "Failed to enter passive mode" msgstr "Échec de l'entrée en mode passif" #: ../src/commands/theme.c:313 #, c-format msgid "failed to remove: %s" msgstr "échec de la suppression: %s" #: ../src/commands/theme.c:333 msgid "no such theme in the database" msgstr "aucun thème de ce type dans la base de données" #: ../src/commands/theme.c:353 msgid "theme installed (use 'set' to activate it)" msgstr "thème installé (utilisez 'set' pour l'activer)" #: ../src/commands/theme.c:356 msgid "failed to install the theme :-(" msgstr "échec de l'installation du thème :-(" #: ../src/commands/theme.c:404 msgid "non-existent" msgstr "inexistant" #: ../src/commands/theme.c:417 msgid "theme activated" msgstr "thème activé" #: ../src/events/account.cpp:56 #, c-format msgid "%s%s%c %s%s@%s%s has logged into a new account %s%s%c" msgstr "%s%s%c %s%s@%s%s s'est connecté à un nouveau compte %s%s%c" #: ../src/events/account.cpp:69 #, c-format msgid "%s%s%c %s%s@%s%s has logged out of their account" msgstr "%s%s%c %s%s@%s%s s'est déconnecté de son compte" #: ../src/events/away.cpp:55 #, c-format msgid "%s%s%c %s%s@%s%s is no longer marked as being away!" msgstr "%s%s%c %s%s@%s%s n'est plus marqué comme étant absent!" #: ../src/events/away.cpp:68 #, c-format msgid "%s%s%c %s%s@%s%s has been marked as being away (%s)" msgstr "%s%s%c %s%s@%s%s a été marqué comme étant absent (%s)" #: ../src/events/channel.cpp:100 #, c-format msgid "Homepage for %s%s%s%c%s: %s" msgstr "Page d'accueil de %s%s%s%c%s: %s" #: ../src/events/channel.cpp:223 #, c-format msgid "%s%s%c %s%s@%s%s has joined %s%s%c" msgstr "%s%s%c %s%s@%s%s a rejoint %s%s%c" #: ../src/events/channel.cpp:308 #, c-format msgid "%s was kicked from %s%s%c by %s%s%c %s%s%s" msgstr "%s a été expulsé de %s%s%c par %s%s%c %s%s%s" #. #. * User mode #. #: ../src/events/channel.cpp:580 #, c-format msgid "Mode change %s%s%s for user %c%s%c" msgstr "Changement de mode %s%s%s pour l'utilisateur %c%s%c" #: ../src/events/channel.cpp:588 #, c-format msgid "mode/%s%s%s%c%s %s%s%s by %s%s%c" msgstr "mode/%s%s%s%c%s %s%s%s par %s%s%c" #: ../src/events/channel.cpp:703 #, c-format msgid "%s%s%c is now known as %s %s%s%c" msgstr "%s%s%c est maintenant connu sous le nom de %s %s%s%c" #: ../src/events/channel.cpp:785 #, c-format msgid "%s%s%c %s%s@%s%s has left %s%s%c %s%s%s" msgstr "%s%s%c %s%s@%s%s a quitté %s%s%c %s%s%s" #: ../src/events/channel.cpp:916 #, c-format msgid "%s%s%c %s%s@%s%s has quit %s%s%s" msgstr "%s%s%c %s%s@%s%s est parti %s%s%s" #: ../src/events/channel.cpp:1011 #, c-format msgid "Topic for %s%s%s%c%s: %s" msgstr "Sujet pour %s%s%s%c%s: %s" #: ../src/events/channel.cpp:1071 #, c-format msgid "%c%s%c changed the topic of %c%s%c to: %s" msgstr "%c%s%c a changé le sujet de %c%s%c en: %s" #: ../src/events/channel.cpp:1149 #, c-format msgid "Topic set by %c%s%c %s%s@%s%s %s%s%s" msgstr "Le sujet a été défini par %c%s%c %s%s@%s%s %s%s%s" #: ../src/events/channel.cpp:1155 #, c-format msgid "Topic set by %c%s%c %s%s%s" msgstr "Le sujet a été défini par %c%s%c %s%s%s" #: ../src/events/chghost.cpp:61 #, c-format msgid "%s%s%s has changed their hostname to %s%s@%s%s" msgstr "%s%s%s a changé son nom d'hôte en %s%s@%s%s" #: ../src/events/invite.cpp:56 msgid "The invitation has been passed onto the end user" msgstr "L'invitation a été transmise à l'utilisateur final" #: ../src/events/invite.cpp:103 #, c-format msgid "%c%s%c %s%s@%s%s invites you to %c%s%c" msgstr "%c%s%c %s%s@%s%s vous invite à %c%s%c" #. #. * TODO: Write to the channels where the user #. * that's doing the invite is in #. #: ../src/events/invite.cpp:113 #, c-format msgid "%c%s%c %s%s@%s%s invites %c%s%c to %c%s%c" msgstr "%c%s%c %s%s@%s%s invite %c%s%c à %c%s%c" #: ../src/events/misc.cpp:246 #, c-format msgid "Channel %s%s%s%c%s created %s" msgstr "La chaîne %s%s%s%c%s a été créée le %s" #: ../src/events/misc.cpp:303 #, c-format msgid "mode/%s%s%s%c%s %s%s%s" msgstr "mode/%s%s%s%c%s %s%s%s" #: ../src/events/misc.cpp:354 #, c-format msgid "Channel forwarding from %c%s%c to %c%s%c" msgstr "Transfert de canal de %c%s%c vers %c%s%c" #: ../src/events/misc.cpp:431 #, c-format msgid "Nickname %c%s%c is already in use" msgstr "Le surnom %c%s%c est déjà utilisé" #: ../src/events/misc.cpp:439 msgid "Alternative nickname already tested. Disconnecting..." msgstr "Surnom alternatif déjà testé. Déconnexion..." #: ../src/events/misc.cpp:444 #, c-format msgid "Attempting to use alt_nick (%s) instead..." msgstr "Tentative d'utilisation de alt_nick (%s) à la place..." #: ../src/events/misc.cpp:452 msgid "Disconnecting..." msgstr "Déconnexion..." #: ../src/events/misc.cpp:513 msgid "You're now an IRC operator!" msgstr "Vous êtes maintenant un opérateur IRC!" #: ../src/include/commandhelp.h:44 msgid "usage: /admin [target]" msgstr "usage: /admin [cible]" #: ../src/include/commandhelp.h:46 msgid "" "The admin command is used to find information about the administrator\n" "of the given server, or current server if 'target' parameter is omit-\n" "ted." msgstr "" "La commande admin est utilisée pour rechercher des informations sur\n" "l'administrateur du serveur donné, ou du serveur actuel si le para-\n" "mètre 'target' est omis." #: ../src/include/commandhelp.h:53 ../src/include/commandhelp.h:201 #: ../src/include/commandhelp.h:282 msgid "usage:" msgstr "" #: ../src/include/commandhelp.h:60 msgid "Send announcements on IRC." msgstr "Envoyer des annonces sur IRC." #: ../src/include/commandhelp.h:65 msgid "usage: /away [reason]" msgstr "usage: /away [raison]" #: ../src/include/commandhelp.h:67 msgid "" "Marks yourself as being away (with a reason). If the reason is omitted\n" "you will be marked as no longer being away." msgstr "" "Vous marque comme étant absent (avec une raison). Si la raison est\n" "omise, vous serez marqué comme n'étant plus absent." #: ../src/include/commandhelp.h:73 msgid "usage: /ban " msgstr "usage: /ban " #: ../src/include/commandhelp.h:75 msgid "" "Sets a channel ban which rejects all users whose 'nick!user@host'\n" "matches the provided mask from joining the channel. Wildcards are okay\n" "and the active window must be an IRC channel." msgstr "" "Définit une interdiction de canal qui rejette tous les utilisateurs\n" "dont le 'nick!user@host' correspond au masque fourni de rejoindre le\n" "canal. Les caractères génériques sont acceptables et la fenêtre active\n" "doit être un canal IRC." #: ../src/include/commandhelp.h:82 msgid "usage: /banlist [channel]" msgstr "usage: /banlist [canal]" #: ../src/include/commandhelp.h:84 msgid "" "Outputs a channel's banlist. If the channel argument is left empty and\n" "the active window is an IRC channel, Swirc will output the banlist for\n" "that channel." msgstr "" "Affiche la banlist d'un canal. Si l'argument du canal est laissé vide\n" "et que la fenêtre active est un canal IRC, Swirc affichera la banlist\n" "pour ce canal." #: ../src/include/commandhelp.h:91 msgid "usage: /beep " msgstr "usage: /beep " #: ../src/include/commandhelp.h:93 msgid "Send beeps. (ICB only)" msgstr "Envoyer des bips. (ICB uniquement)" #: ../src/include/commandhelp.h:98 msgid "usage: /boot " msgstr "usage: /boot " #: ../src/include/commandhelp.h:100 msgid "Kick a user out of your group. (ICB only)" msgstr "Expulsez un utilisateur de votre groupe. (ICB uniquement)" #: ../src/include/commandhelp.h:105 msgid "usage: /chanserv <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:107 msgid "" "Communicate with your IRC network's channel service. If the initial\n" "argument equals to '--', then the value of setting 'chanserv_host' is\n" "used as a service hostname." msgstr "" #: ../src/include/commandhelp.h:118 msgid "usage: /ctcp " msgstr "" #: ../src/include/commandhelp.h:120 msgid "" "Send a CTCP query to 'target', which is either a nickname or an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:123 msgid "Query can be either:" msgstr "" #: ../src/include/commandhelp.h:131 msgid "usage: /cap [ls | list]" msgstr "" #: ../src/include/commandhelp.h:133 msgid "" "Lists the (IRCv3) capabilities supported by the server and/or the\n" "capabilities associated with the active connection." msgstr "" #: ../src/include/commandhelp.h:139 msgid "usage: /cleartoasts" msgstr "" #: ../src/include/commandhelp.h:141 msgid "" "On Windows Swirc sends toast notifications. By running this command\n" "all notifications associated with Swirc will be cleared." msgstr "" #: ../src/include/commandhelp.h:147 msgid "usage: /close" msgstr "" #: ../src/include/commandhelp.h:149 msgid "" "Closes the active window. It's not possible to close the status\n" "window. And while connected it's not possible to close a channel, in\n" "that case instead use '/part'." msgstr "" #: ../src/include/commandhelp.h:156 msgid "usage: /colormap" msgstr "" #: ../src/include/commandhelp.h:158 msgid "Outputs information about colors." msgstr "" #: ../src/include/commandhelp.h:163 msgid "usage: /connect [-tls] " msgstr "" #: ../src/include/commandhelp.h:165 msgid "Connect to given server." msgstr "" #: ../src/include/commandhelp.h:167 msgid "" "If the port is omitted port 6667 will be chosen. And if the port is\n" "7326 ICB mode is turned on automatically. Further, if the port is 6697\n" "Swirc attempts to initiate a TLS/SSL connection, as well as if '-tls'\n" "is entered." msgstr "" #: ../src/include/commandhelp.h:172 msgid "" "It is possible to connect to a certain IRC network by only entering\n" "the network name. For example: '/connect -tls libera', will connect to\n" "Libera Chat using an encrypted connection. Preprogrammed network names\n" "are:" msgstr "" #: ../src/include/commandhelp.h:192 msgid "usage: /cycle [channel]" msgstr "" #: ../src/include/commandhelp.h:194 msgid "" "Cycle a channel, i.e. '/part' plus '/join'. If the channel argument is\n" "omitted and the active window is an IRC channel, the client will cycle\n" "that channel." msgstr "" #: ../src/include/commandhelp.h:209 msgid "" "Get and send files. Swirc implements its own variant of DCC meaning\n" "it's incompatible with other IRC clients. Transport Layer Security is\n" "forced and for now the DCC feature isn't available in ICB mode." msgstr "" #: ../src/include/commandhelp.h:216 msgid "usage: /deop " msgstr "" #: ../src/include/commandhelp.h:218 msgid "Remove the channel operator privilege from another user." msgstr "" #: ../src/include/commandhelp.h:223 msgid "usage: /devoice " msgstr "" #: ../src/include/commandhelp.h:225 msgid "Remove the channel voice privilege from another user." msgstr "" #: ../src/include/commandhelp.h:230 msgid "usage: /die [--I-am-sure]" msgstr "" #: ../src/include/commandhelp.h:232 msgid "" "An IRC operator can use this command to shutdown the server. Please\n" "confirm that this is what you really want by typing '--I-am-sure'." msgstr "" #: ../src/include/commandhelp.h:238 msgid "usage: /disconnect [message]" msgstr "" #: ../src/include/commandhelp.h:240 msgid "" "Disconnect from IRC, but don't quit the program. A disconnect message\n" "is optional." msgstr "" #: ../src/include/commandhelp.h:246 msgid "usage: /echo " msgstr "" #: ../src/include/commandhelp.h:248 msgid "Writes text to the current window without sending anything." msgstr "" #: ../src/include/commandhelp.h:252 msgid "Echo 'Hello World':" msgstr "" #: ../src/include/commandhelp.h:253 msgid " /echo Hello World" msgstr "" #: ../src/include/commandhelp.h:258 msgid "usage: /exlist [channel]" msgstr "" #: ../src/include/commandhelp.h:260 msgid "" "Outputs a channel's exception list. An exception mask (+e) overrides a\n" "ban mask. If the channel argument is omitted and the active window is\n" "an IRC channel, the client will output the exception list for that\n" "channel." msgstr "" #: ../src/include/commandhelp.h:268 msgid "usage: /fetchdic [name]" msgstr "" #: ../src/include/commandhelp.h:270 msgid "" "Fetches spelling dictionaries. If the name argument is omitted Swirc\n" "will output a list of available dictionaries. The list is obtained\n" "remotely which means its contents can be updated at any time." msgstr "" #: ../src/include/commandhelp.h:276 msgid "Fetch the american english dictionary:" msgstr "" #: ../src/include/commandhelp.h:295 msgid "" "Retrieve and store files on a FTP server. (The communication is done\n" "in clear text, i.e. unencrypted.)" msgstr "" #: ../src/include/commandhelp.h:301 msgid "usage: /gline [ :]" msgstr "" #: ../src/include/commandhelp.h:303 msgid "Network-wide bans." msgstr "" #: ../src/include/commandhelp.h:305 msgid "" "When a client matches a G-line it cannot connect to ANY server on the\n" "IRC network for 'duration' seconds. If the duration is zero then the\n" "G-line will be permanent." msgstr "" #: ../src/include/commandhelp.h:309 msgid "(If no duration and no reason is given the G-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:318 msgid "usage: /group " msgstr "" #: ../src/include/commandhelp.h:320 msgid "Changes ICB group." msgstr "" #: ../src/include/commandhelp.h:325 msgid "usage: /help [command]" msgstr "" #: ../src/include/commandhelp.h:327 msgid "Outputs a list of all available commands." msgstr "" #: ../src/include/commandhelp.h:328 msgid "(Or help for a specific command.)" msgstr "" #: ../src/include/commandhelp.h:333 msgid "usage: /ignore [regex]" msgstr "" #: ../src/include/commandhelp.h:335 msgid "" "Ignores all 'nick!user@host' that matches the given regular\n" "expression, this by using the POSIX basic regular expression\n" "grammar. This command isn't to be used by beginners and I advice you\n" "to be careful when using it. I highly recommend the use of:" msgstr "" #: ../src/include/commandhelp.h:340 msgid " 1. ^ Matches the starting position within the string, if it is" msgstr "" #: ../src/include/commandhelp.h:341 msgid " the first character of the regular expression." msgstr "" #: ../src/include/commandhelp.h:342 msgid " 2. $ Matches the ending position of the string, if it is the last" msgstr "" #: ../src/include/commandhelp.h:343 msgid " character of the regular expression." msgstr "" #: ../src/include/commandhelp.h:347 msgid "Ignore nickname 'troll':" msgstr "" #: ../src/include/commandhelp.h:350 msgid "Ignore all users with username 'troll':" msgstr "" #: ../src/include/commandhelp.h:353 msgid "Ignore all users with hostname 'insecure.org':" msgstr "" #: ../src/include/commandhelp.h:356 msgid "Ignore all users with a Chinese domain (.cn):" msgstr "" #: ../src/include/commandhelp.h:362 msgid "usage: /ilist [channel]" msgstr "" #: ../src/include/commandhelp.h:364 msgid "" "Outputs a channel's invitation list. An invitation mask (+I) overrides\n" "the invite-only flag (+i). If the channel argument is omitted and the\n" "active window is an IRC channel, the client will output the invitation\n" "list for that channel." msgstr "" #: ../src/include/commandhelp.h:372 msgid "usage: /info [target]" msgstr "" #: ../src/include/commandhelp.h:374 msgid "The info command returns information about the server." msgstr "" #: ../src/include/commandhelp.h:379 msgid "usage: /invite " msgstr "" #: ../src/include/commandhelp.h:381 msgid "Invites 'targ_nick' to a channel." msgstr "" #: ../src/include/commandhelp.h:386 msgid "usage: /ison [nick2] [nick3] [...]" msgstr "" #: ../src/include/commandhelp.h:388 msgid "Checks whether users are on IRC." msgstr "" #: ../src/include/commandhelp.h:393 msgid "usage: /join [key]" msgstr "" #: ../src/include/commandhelp.h:395 msgid "Joins a channel (optionally by using a key)." msgstr "" #: ../src/include/commandhelp.h:399 msgid "Join a channel with name 'libera':" msgstr "" #: ../src/include/commandhelp.h:402 msgid "Join a key-protected channel:" msgstr "" #: ../src/include/commandhelp.h:408 msgid "usage: /kick [reason]" msgstr "" #: ../src/include/commandhelp.h:410 msgid "" "Kicks one or more users out of a channel. The users are given in a\n" "comma-separated list. A reason is optional and the active window must\n" "be an IRC channel." msgstr "" #: ../src/include/commandhelp.h:417 msgid "usage: /kickban [reason]" msgstr "" #: ../src/include/commandhelp.h:419 msgid "" "Set a channel ban with given 'mask' and kick the user 'nick' out of a\n" "channel. A reason is optional and the active window must be an IRC\n" "channel." msgstr "" #: ../src/include/commandhelp.h:426 msgid "usage: /kill " msgstr "" #: ../src/include/commandhelp.h:428 msgid "Disconnect a user from the connected network." msgstr "" #: ../src/include/commandhelp.h:429 msgid "(Requires IRC op privilege.)" msgstr "" #: ../src/include/commandhelp.h:434 msgid "usage: /kline [ :]" msgstr "" #: ../src/include/commandhelp.h:436 msgid "Server-local bans." msgstr "" #: ../src/include/commandhelp.h:438 msgid "" "When a client matches a K-line it cannot connect to the local server\n" "for 'duration' seconds. If the duration is zero then the K-line will\n" "be permanent." msgstr "" #: ../src/include/commandhelp.h:442 msgid "(If no duration and no reason is given the K-line is deleted.)" msgstr "" #: ../src/include/commandhelp.h:451 msgid "usage: /list [min_users][,pattern][...]]" msgstr "" #: ../src/include/commandhelp.h:453 msgid "" "Lists channels and their topics. Without any arguments the list is\n" "HUGE. For example, '/list >1500' will only list channels that have\n" "more than 1500 users." msgstr "" #: ../src/include/commandhelp.h:457 msgid "" "Depending on the IRC server software used by your network the usage\n" "may differ." msgstr "" #: ../src/include/commandhelp.h:463 msgid "usage: /me " msgstr "" #: ../src/include/commandhelp.h:465 msgid "Send an 'action' message. (Used to simulate role playing on IRC.)" msgstr "" #: ../src/include/commandhelp.h:470 msgid "usage: /mode [...]" msgstr "" #: ../src/include/commandhelp.h:472 msgid "Alter modes." msgstr "" #: ../src/include/commandhelp.h:476 msgid " o - give/take the channel operator privilege" msgstr "" #: ../src/include/commandhelp.h:477 msgid " v - give/take the channel voice privilege" msgstr "" #: ../src/include/commandhelp.h:479 msgid " i - invite-only channel" msgstr "" #: ../src/include/commandhelp.h:480 msgid " m - moderated channel" msgstr "" #: ../src/include/commandhelp.h:481 msgid " n - no messages to a channel from clients on the outside" msgstr "" #: ../src/include/commandhelp.h:482 msgid " p - private channel" msgstr "" #: ../src/include/commandhelp.h:483 msgid " s - secret channel" msgstr "" #: ../src/include/commandhelp.h:484 msgid " t - topic settable by channel operators only" msgstr "" #: ../src/include/commandhelp.h:486 msgid " k - set/remove the channel key (password)" msgstr "" #: ../src/include/commandhelp.h:487 msgid " l - set/remove the channel user limit" msgstr "" #: ../src/include/commandhelp.h:489 msgid " b - set/remove a ban mask" msgstr "" #: ../src/include/commandhelp.h:490 msgid " e - set/remove an exception mask to override a ban mask" msgstr "" #: ../src/include/commandhelp.h:491 msgid " I - set/remove an invitation mask to override the invite-only flag" msgstr "" #: ../src/include/commandhelp.h:495 msgid " i - marks a user as invisible" msgstr "" #: ../src/include/commandhelp.h:496 msgid " w - the user receives wallops messages" msgstr "" #: ../src/include/commandhelp.h:500 msgid " Give channel operator privilege to 'Companion' in #foo:" msgstr "" #: ../src/include/commandhelp.h:503 msgid " Restrict messaging to channel #linux:" msgstr "" #: ../src/include/commandhelp.h:506 msgid " Limit user count for #freenode to 10:" msgstr "" #: ../src/include/commandhelp.h:509 msgid " Deny all users with hostname spammers.net from joining #chatzone:" msgstr "" #: ../src/include/commandhelp.h:512 msgid " Turn on reception of WALLOPS messages:" msgstr "" #: ../src/include/commandhelp.h:518 msgid "usage: /msg " msgstr "" #: ../src/include/commandhelp.h:520 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels." msgstr "" #: ../src/include/commandhelp.h:526 msgid "usage: /nick " msgstr "" #: ../src/include/commandhelp.h:528 msgid "Sets your nickname." msgstr "" #: ../src/include/commandhelp.h:533 msgid "usage: /nickserv <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:535 msgid "Communicate with your IRC network's nickname service." msgstr "" #: ../src/include/commandhelp.h:537 msgid "If the initial argument equals to '--' then the:" msgstr "" #: ../src/include/commandhelp.h:539 msgid " 1) Value of setting 'nickserv_host' will be used as service" msgstr "" #: ../src/include/commandhelp.h:540 msgid " hostname." msgstr "" #: ../src/include/commandhelp.h:541 msgid " 2) Command call won't be added to the command history provided" msgstr "" #: ../src/include/commandhelp.h:542 msgid " that the second argument is 'identify'." msgstr "" #: ../src/include/commandhelp.h:544 msgid "" "The correct service hostname is not always the same as the visible\n" "hostname of NickServ. FYI at the AnonOps IRC network the visible\n" "hostname of NickServ is anonops.in (when this text was written)\n" "but you should use 'services.anonops.com'. As a fallback:" msgstr "" #: ../src/include/commandhelp.h:548 msgid "" "'/query NickServ' can be used in order to communicate with the\n" "service." msgstr "" #: ../src/include/commandhelp.h:554 msgid "usage: /notice " msgstr "" #: ../src/include/commandhelp.h:556 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels. (In notice form.)" msgstr "" #: ../src/include/commandhelp.h:562 msgid "usage: /op " msgstr "" #: ../src/include/commandhelp.h:564 msgid "Gives the channel operator privilege to another user." msgstr "" #: ../src/include/commandhelp.h:569 msgid "usage: /oper " msgstr "" #: ../src/include/commandhelp.h:571 msgid "Identifies yourself as an IRC operator." msgstr "" #: ../src/include/commandhelp.h:576 msgid "usage: /part [channel] [message]" msgstr "" #: ../src/include/commandhelp.h:578 msgid "" "Leaves a channel (optionally with a message). If the command is called\n" "without any arguments and the current window is an IRC channel, that\n" "channel will be the target." msgstr "" #: ../src/include/commandhelp.h:584 msgid " Leave channel #chatzone with message 'bye':" msgstr "" #: ../src/include/commandhelp.h:585 msgid " /part #chatzone bye" msgstr "" #: ../src/include/commandhelp.h:590 msgid "usage: /passmod " msgstr "" #: ../src/include/commandhelp.h:592 msgid "Pass the ICB moderation privilege to another group member." msgstr "" #: ../src/include/commandhelp.h:597 msgid "usage: /qbot <[service hostname | --]> [...]" msgstr "" #: ../src/include/commandhelp.h:599 msgid "" "Communicate with QuakeNet's network service, the Q bot. If the initial\n" "argument equals to '--', then the value of setting 'qbot_host' is used\n" "as a service hostname." msgstr "" #: ../src/include/commandhelp.h:606 msgid "usage: /query [nick]" msgstr "" #: ../src/include/commandhelp.h:608 msgid "" "Starts a private conversation with 'nick'. If 'nick' is omitted and\n" "the active window is a private conversation, the action will be to\n" "close it." msgstr "" #: ../src/include/commandhelp.h:615 msgid "usage: /quit [message]" msgstr "" #: ../src/include/commandhelp.h:617 msgid "" "Disconnect from IRC and quit the program. A disconnect message is\n" "optional." msgstr "" #: ../src/include/commandhelp.h:623 msgid "usage: /rehash" msgstr "" #: ../src/include/commandhelp.h:625 msgid "" "The rehash command is an administrative command which can be used by\n" "an IRC operator to force the server to re-read and process its conf-\n" "iguration file." msgstr "" #: ../src/include/commandhelp.h:632 msgid "usage: /resize" msgstr "" #: ../src/include/commandhelp.h:634 msgid "" "Resize the terminal. For example, Windows doesn't send 'SIGWINCH',\n" "instead this command can be used. First resize the window then run\n" "this command." msgstr "" #: ../src/include/commandhelp.h:641 msgid "usage: /restart [--I-am-sure]" msgstr "" #: ../src/include/commandhelp.h:643 msgid "" "An IRC operator can use the 'restart'-command to force the server to\n" "restart itself." msgstr "" #: ../src/include/commandhelp.h:649 msgid "usage: /rules" msgstr "" #: ../src/include/commandhelp.h:651 msgid "" "Outputs network/server rules. Not all IRC server software supports\n" "this command. (It's actually quite rare.)" msgstr "" #: ../src/include/commandhelp.h:657 msgid "usage: /sasl [...]" msgstr "" #: ../src/include/commandhelp.h:659 msgid "Simple Authentication and Security Layer." msgstr "" #: ../src/include/commandhelp.h:660 msgid "Operation can be either:" msgstr "" #: ../src/include/commandhelp.h:672 msgid "" "SASL is a method that lets you identify with NickServ during the\n" "connection process eliminating the need to do it manually." msgstr "" #: ../src/include/commandhelp.h:674 msgid "(To use SASL you must register your nickname.)" msgstr "" #: ../src/include/commandhelp.h:678 msgid " Identification using mechanism ecdsa-nist256p-challenge:" msgstr "" #: ../src/include/commandhelp.h:686 msgid "" " (The only IRC network that I know of that is supporting this\n" " mechanism is Libera Chat.)" msgstr "" #: ../src/include/commandhelp.h:689 msgid " Identification using mechanism 'plain':" msgstr "" #: ../src/include/commandhelp.h:698 msgid "usage: /say " msgstr "" #: ../src/include/commandhelp.h:700 msgid "" "Say a message. This command can be used if you want say something with\n" "a leading command-character, i.e. a slash." msgstr "" #: ../src/include/commandhelp.h:705 msgid " /say // A single-line comment in C++" msgstr "" #: ../src/include/commandhelp.h:710 msgid "usage: /servlist [ []]" msgstr "" #: ../src/include/commandhelp.h:712 msgid "" "Lists services currently connected to your IRC network. Arguments,\n" "if given, can be used to restrict the output result." msgstr "" #: ../src/include/commandhelp.h:722 msgid "usage: /servstats [ []]" msgstr "" #: ../src/include/commandhelp.h:724 msgid "This command is used to query statistics of a certain IRC server." msgstr "" #: ../src/include/commandhelp.h:729 msgid "usage: /set [[setting] [value]]" msgstr "" #: ../src/include/commandhelp.h:731 msgid "Alter Swirc settings." msgstr "" #: ../src/include/commandhelp.h:736 msgid "" " Bools are case insensitive and can have one of the following\n" " values:" msgstr "" #: ../src/include/commandhelp.h:738 msgid " - on, true or yes" msgstr "" #: ../src/include/commandhelp.h:739 msgid " - off, false or no" msgstr "" #: ../src/include/commandhelp.h:742 msgid "" " Integers. Swirc implements a min/max value for each integer in\n" " order to keep its value safe. The error log will tell if the\n" " restrictions for an integer aren't within limits and, if so, that\n" " a preprogrammed fallback value is being used instead." msgstr "" #: ../src/include/commandhelp.h:748 msgid " An arbitrary sequence of characters" msgstr "" #: ../src/include/commandhelp.h:752 msgid " Output the current values of all settings:" msgstr "" #: ../src/include/commandhelp.h:753 msgid " /set (without any arguments)" msgstr "" #: ../src/include/commandhelp.h:755 msgid " Turn beeps on/off:" msgstr "" #: ../src/include/commandhelp.h:762 msgid "usage: /squery " msgstr "" #: ../src/include/commandhelp.h:764 msgid "" "This command is used similarly to '/msg'. The only difference is that\n" "the recipient MUST be a service." msgstr "" #: ../src/include/commandhelp.h:770 msgid "usage: /stats [channel]" msgstr "" #: ../src/include/commandhelp.h:772 msgid "" "Outputs a channel's user statistics. If the channel argument is left\n" "empty and the active window is an IRC channel, Swirc will output the\n" "user statistics for that channel." msgstr "" #: ../src/include/commandhelp.h:779 msgid "usage: /theme [install | list-remote | set ]" msgstr "" #: ../src/include/commandhelp.h:781 msgid "Management of themes on-the-fly." msgstr "" #: ../src/include/commandhelp.h:785 msgid " Install a theme named 'bx':" msgstr "" #: ../src/include/commandhelp.h:788 msgid " List all available themes:" msgstr "" #: ../src/include/commandhelp.h:791 msgid " Activate an installed theme with name 'bx':" msgstr "" #: ../src/include/commandhelp.h:797 msgid "usage: /time " msgstr "" #: ../src/include/commandhelp.h:799 msgid "" "Send a CTCP TIME request to 'target', which is either a nickname or an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:805 msgid "usage: /topic [new topic]" msgstr "" #: ../src/include/commandhelp.h:807 msgid "" "Sets a new topic for a channel. If 'new topic' is omitted the action\n" "will be to display the current topic. (The active window must be an\n" "IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:814 msgid "usage: /unban " msgstr "" #: ../src/include/commandhelp.h:816 msgid "Removes a channel ban. (The active window must be an IRC channel.)" msgstr "" #: ../src/include/commandhelp.h:826 msgid "usage: /unignore [#]" msgstr "" #: ../src/include/commandhelp.h:828 msgid "Deletes a regular expression from the ignore list." msgstr "" #: ../src/include/commandhelp.h:837 msgid "usage: /version " msgstr "" #: ../src/include/commandhelp.h:839 msgid "" "Send a CTCP VERSION request to 'target', which is either a nickname or\n" "an IRC channel." msgstr "" #: ../src/include/commandhelp.h:845 msgid "usage: /voice " msgstr "" #: ../src/include/commandhelp.h:847 msgid "Gives the channel voice privilege to another user." msgstr "" #: ../src/include/commandhelp.h:852 msgid "usage: /wallops " msgstr "" #: ../src/include/commandhelp.h:854 msgid "" "The 'wallops'-command is used to send a message to all currently\n" "connected users who have set the 'w' user mode for themselves." msgstr "" #: ../src/include/commandhelp.h:860 msgid "usage: /who " msgstr "" #: ../src/include/commandhelp.h:862 msgid "" "Generates a query which returns a list of information which matches\n" "the provided 'mask'." msgstr "" #: ../src/include/commandhelp.h:867 msgid " Show the Libera Chat crew:" msgstr "" #: ../src/include/commandhelp.h:870 msgid " Show users with a German domain name:" msgstr "" #: ../src/include/commandhelp.h:876 msgid "usage: /whois " msgstr "" #: ../src/include/commandhelp.h:878 msgid "Asks after information about another user." msgstr "" #: ../src/include/commandhelp.h:883 msgid "usage: /wholeft" msgstr "" #: ../src/include/commandhelp.h:885 msgid "" "By using this command you can see who left during a network server\n" "split. The command takes no arguments and the active window must be an\n" "IRC channel." msgstr "" #: ../src/include/commandhelp.h:892 msgid "usage: /znc [*module] " msgstr "" #: ../src/include/commandhelp.h:894 msgid "" "Simplifies the communication with ZNC which is a popular\n" "'IRC bouncer'." msgstr "" #: ../src/include/commandhelp.h:899 msgid " Output your ZNC version:" msgstr "" #: ../src/include/commandhelp.h:902 msgid " Identical to the previous example:" msgstr "" #: ../src/interpreter.cpp:56 ../src/interpreter.cpp:87 #, c-format msgid "%s: fatal: string was truncated" msgstr "" #: ../src/io-loop.c:596 #, c-format msgid " Swirc %s by %s" msgstr " Swirc %s par %s" #: ../src/io-loop.c:597 #, c-format msgid " Compiled on %s%s %s%s" msgstr " Compilé %s%s %s%s" #: ../src/io-loop.c:603 #, c-format msgid "Current language %s%s%s" msgstr "Langue actuelle %s%s%s" #: ../src/io-loop.c:606 #, c-format msgid "Program settings are stored in %s%s%s" msgstr "Les réglages du programme sont stockés dans %s%s%s" #: ../src/io-loop.c:608 #, c-format msgid "%c%hd%c color pairs have been initialized" msgstr "%c%hd%c paires de couleurs ont été initialisées" #: ../src/io-loop.c:610 msgid "Type /help for a list of commands; or /help " msgstr "Tapez /help pour une liste de commandes; ou /help " #: ../src/io-loop.c:612 msgid "for help of a specific command" msgstr "pour l'aide d'une commande spécifique" #: ../src/io-loop.c:613 msgid "Type F1 for keys" msgstr "Tapez F1 pour les touches du clavier" #: ../src/io-loop.c:615 #, c-format msgid "Error log size %s%.1f KB%s" msgstr "Taille du journal des erreurs %s%.1f KB%s" #: ../src/irc.c:332 #, c-format msgid "%s: no nickname" msgstr "" #: ../src/irc.c:451 #, c-format msgid "%s: unsupported extension" msgstr "" #: ../src/irc.c:648 #, c-format msgid "Unknown normal event: %s" msgstr "" #: ../src/irc.c:659 #, c-format msgid "Unknown numeric event: %s" msgstr "" #: ../src/irc.c:667 #, c-format msgid "Erroneous event: %s" msgstr "" #: ../src/log.c:217 #, c-format msgid "Logging for window (refnum: %d) now off" msgstr "Journalisation pour la fenêtre (refnum: %d) désormais désactivée" #: ../src/log.c:221 #, c-format msgid "Logging for window (refnum: %d) now on" msgstr "Journalisation pour la fenêtre (refnum: %d) désormais activée" #: ../src/main.cpp:84 #, c-format msgid "A duplicate of option -%c found" msgstr "" #: ../src/main.cpp:144 msgid "" "\n" "Options:\n" "\n" msgstr "" "\n" "Options:\n" "\n" #: ../src/main.cpp:145 msgid " -4 Use IPv4 addresses only\n" msgstr " -4 IPv4 seulement\n" #: ../src/main.cpp:146 msgid " -6 Use IPv6 addresses only\n" msgstr " -6 IPv6 seulement\n" #: ../src/main.cpp:147 msgid " -?, --help Output help\n" msgstr " -?, --help Afficher l'aide\n" #: ../src/main.cpp:148 msgid " -C Do not change color definitions\n" msgstr " -C Ne pas changer les couleurs\n" #: ../src/main.cpp:149 msgid " -P Permanently disable SASL authentication\n" msgstr " -P Désactiver l'authentification SASL\n" #: ../src/main.cpp:150 msgid " -R Disable TLS/SSL peer verification\n" msgstr "" " -R Désactivation de la vérification du certificat TLS/" "SSL\n" #: ../src/main.cpp:151 msgid " -S Force TLS\n" msgstr " -S Forcer TLS\n" #: ../src/main.cpp:156 msgid " -W Equal effect as flag 'p' but non-interactive\n" msgstr "" " -W Effet égal à celui du drapeau 'p' mais non " "interactif\n" #: ../src/main.cpp:157 msgid " -X Disable all IRCv3 extensions\n" msgstr " -X Désactiver toutes les extensions IRCv3\n" #: ../src/main.cpp:158 msgid " -c Connect to IRC server\n" msgstr " -c Se connecter à un serveur IRC\n" #: ../src/main.cpp:159 msgid " -d Debug logging\n" msgstr " -d Activer les journaux de débogage\n" #: ../src/main.cpp:160 msgid " -i Turn on Internet Citizen's Band mode\n" msgstr " -i Activer le mode Internet Citizen's Band\n" #: ../src/main.cpp:161 msgid " -j A comma-separated list of channels to join\n" msgstr "" " -j Liste de salons à rejoinre (séparés par des " "virgules)\n" #: ../src/main.cpp:162 msgid " -n Online nickname\n" msgstr " -n Pseudonyme\n" #: ../src/main.cpp:163 msgid "" " -p Query for server password (for private servers)\n" msgstr "" " -p Requête pour le mot de passe serveur (serveurs " "privés uniquement)\n" #: ../src/main.cpp:164 msgid " -r Your real name\n" msgstr " -r Votre nom\n" #: ../src/main.cpp:165 msgid " -u Your username\n" msgstr " -u Votre utilisateur\n" #: ../src/main.cpp:166 msgid " -v, --version Output Swirc version\n" msgstr " -v, --version Affiche la version de Swirc\n" #: ../src/main.cpp:167 msgid " -x Config file\n" msgstr " -x Fichier de configuration\n" #: ../src/main.cpp:230 msgid "Unhandled exception!" msgstr "" #: ../src/main.cpp:290 #, c-format msgid "Usage: %s [OPTION] ...\n" msgstr "Usage: %s [OPTION] ...\n" #: ../src/main.cpp:380 msgid "forbidden channel name characters" msgstr "" #: ../src/main.cpp:532 #, c-format msgid "%s: -%c: unrecognized option" msgstr "" #: ../src/main.cpp:537 #, c-format msgid "%s: -%c: option argument missing" msgstr "" #: ../src/main.cpp:706 msgid "fatal: failed to initialize signal handling" msgstr "" #: ../src/main.cpp:728 msgid "fatal: running the program with root privileges is forbidden" msgstr "" #: ../src/main.cpp:750 msgid "Initialization of the Curses library not possible" msgstr "" #: ../src/messagetags.c:150 #, c-format msgid "%s: server time error" msgstr "" #: ../src/nestHome.c:143 #, c-format msgid "%s exists. However; it isn't a directory." msgstr "" #: ../src/nestHome.c:147 ../src/nestHome.c:150 msgid "Cannot make directory" msgstr "" #: ../src/nestHome.c:177 msgid "Please decrypt your SASL password..." msgstr "" #: ../src/nestHome.c:178 msgid "(One attempt)" msgstr "" #: ../src/nestHome.c:186 msgid "Password: " msgstr "" #: ../src/nestHome.c:216 msgid "Decryption failed" msgstr "" #: ../src/nestHome.c:219 msgid "Wrong password" msgstr "" #: ../src/nestHome.c:230 msgid "The password seems reasonable" msgstr "" #: ../src/nestHome.c:242 msgid "Press " msgstr "" #: ../src/nestHome.c:267 ../src/nestHome.c:278 ../src/nestHome.c:337 #, c-format msgid "%s exists -- but isn't a regular file." msgstr "" #: ../src/nestHome.c:270 ../src/nestHome.c:345 #, c-format msgid "%s no such file or directory. Exiting..." msgstr "" #: ../src/nestHome.c:301 msgid "Warning!" msgstr "" #: ../src/nestHome.c:302 msgid "" "A decrypted SASL password was found in the read configuration file -- " "cannot continue!" msgstr "" #: ../src/nestHome.c:333 msgid "The item 'theme' in the user configuration file holds no data. Error." msgstr "" #: ../src/nestHome.c:358 msgid "Can't resolve the home path!" msgstr "" #: ../src/network.cpp:183 msgid "Hostname checking failed!" msgstr "La vérification du nom d'hôte a échoué!" #: ../src/network.cpp:185 msgid "Hostname checking OK!" msgstr "Vérification du nom d'hôte OK!" #: ../src/network.cpp:242 msgid "Connected!" msgstr "Connecté!" #: ../src/network.cpp:311 msgid "Get a list of IP addresses completed" msgstr "Obtenir une liste d'adresses IP, c'est fait" #: ../src/network.cpp:539 #, c-format msgid "Connecting to %s (%s)" msgstr "Connexion à %s (%s)" #: ../src/network.cpp:807 msgid "Connection to IRC server lost" msgstr "La connexion au serveur IRC a été perdue" #: ../src/network.cpp:818 msgid "Disconnected" msgstr "Déconnecté" #: ../src/readline.c:623 msgid "--------------- Keys ---------------" msgstr "" #: ../src/readline.c:625 msgid "CTRL+a Move to beginning of line" msgstr "" #: ../src/readline.c:626 msgid "CTRL+e Move to end of line" msgstr "" #: ../src/readline.c:627 msgid "CTRL+b Move cursor backward" msgstr "" #: ../src/readline.c:628 msgid "CTRL+f Move cursor forward" msgstr "" #: ../src/readline.c:629 msgid "CTRL+d Delete" msgstr "" #: ../src/readline.c:630 msgid "CTRL+g Clear readline input" msgstr "" #: ../src/readline.c:631 msgid "CTRL+l Toggle logging on/off" msgstr "" #: ../src/readline.c:632 msgid "CTRL+n Next window" msgstr "" #: ../src/readline.c:633 msgid "CTRL+p Previous window" msgstr "" #: ../src/readline.c:634 msgid "CTRL+w List all windows" msgstr "" #: ../src/readline.c:635 msgid "PG UP Scroll up" msgstr "" #: ../src/readline.c:636 msgid "PG DOWN Scroll down" msgstr "" #: ../src/readline.c:637 msgid "Up arrow History previous" msgstr "" #: ../src/readline.c:638 msgid "Down arrow History next" msgstr "" #: ../src/readline.c:639 msgid "F2 Spell word" msgstr "" #: ../src/readline.c:640 msgid "F3 Scroll nicklist up" msgstr "" #: ../src/readline.c:641 msgid "F4 Scroll nicklist down" msgstr "" #: ../src/readline.c:642 msgid "F11 Close window" msgstr "" #: ../src/readline.c:643 msgid "F12 Close all private conversations" msgstr "" #: ../src/sig-w32.c:26 msgid "Abnormal termination" msgstr "Abandon" #: ../src/sig-w32.c:27 msgid "Floating-point error" msgstr "Exception en point flottant" #: ../src/sig-w32.c:28 msgid "Illegal instruction" msgstr "Instruction non permise" #: ../src/sig-w32.c:29 msgid "Illegal storage access" msgstr "Erreur de segmentation" #: ../src/sig-w32.c:30 msgid "Termination request" msgstr "Complété" #: ../src/spell.cpp:137 ../src/spell.cpp:141 #, c-format msgid "%s: %s not found" msgstr "%s: %s non trouvé" #: ../src/spell.cpp:317 msgid "no more suggestions" msgstr "plus aucune suggestion" #: ../src/spell.cpp:344 msgid "suggestions:" msgstr "suggestions:" #: ../src/spell.cpp:372 #, c-format msgid "%ls is correct" msgstr "%ls est correct" #: ../src/spell.cpp:377 #, c-format msgid "%ls is incorrect" msgstr "%ls est incorrect" #: ../src/statusbar.cpp:222 msgid "Log: " msgstr "Enregistrer: " #: ../src/statusbar.cpp:227 msgid "-- MORE --" msgstr "-- PLUS --" #: ../src/tls-server.cpp:205 msgid "Already accepting DCC connections..." msgstr "Acceptant déjà les connexions DCC..." #: ../src/tls-server.cpp:229 #, c-format msgid "Accepting DCC connections at port: %d" msgstr "Acceptation des connexions DCC sur le port: %d" #: ../src/tls-server.cpp:241 msgid "Out of memory" msgstr "Mémoire insuffisante" #: ../src/tls-server.cpp:250 msgid "Stopped accepting DCC connections" msgstr "Arrêt de l'acceptation des connexions DCC" swirc-3.5.5/po/makecab.sh000077500000000000000000000023301501213070300152010ustar00rootroot00000000000000#!/bin/sh # SPDX-FileCopyrightText: Copyright 2023 Markus Uhlin # SPDX-License-Identifier: ISC if [ "$(whoami)" = "root" ]; then echo "do not run as root" exit 1 fi check_tools() { local _tools="gcab" for _tool in ${_tools}; do printf "checking for %s..." "${_tool}" if [ -x "$(/bin/which ${_tool})" ]; then echo "found" else echo "fatal: not found" exit 1 fi done } check_tools CAB_DATE="$(date +%Y%m%d)" FOLDER="swirc-locales-${CAB_DATE}" MO_NAME="swirc.mo" test -d "${FOLDER}" && rm -frv "${FOLDER}" test -f "${FOLDER}.cab" && rm -fv "${FOLDER}.cab" mkdir -p "${FOLDER}/de/LC_MESSAGES" || exit 1 mkdir -p "${FOLDER}/fi/LC_MESSAGES" || exit 1 mkdir -p "${FOLDER}/fr/LC_MESSAGES" || exit 1 mkdir -p "${FOLDER}/sv/LC_MESSAGES" || exit 1 CP_FLAGS="-fv" test -f "de/${MO_NAME}" && \ cp ${CP_FLAGS} de/${MO_NAME} "${FOLDER}/de/LC_MESSAGES/" test -f "fi/${MO_NAME}" && \ cp ${CP_FLAGS} fi/${MO_NAME} "${FOLDER}/fi/LC_MESSAGES/" test -f "fr/${MO_NAME}" && \ cp ${CP_FLAGS} fr/${MO_NAME} "${FOLDER}/fr/LC_MESSAGES/" test -f "sv/${MO_NAME}" && \ cp ${CP_FLAGS} sv/${MO_NAME} "${FOLDER}/sv/LC_MESSAGES/" gcab -cv "${FOLDER}.cab" "${FOLDER}" test -f "${FOLDER}.cab" && echo "cab successfully created" swirc-3.5.5/po/sv/000077500000000000000000000000001501213070300137115ustar00rootroot00000000000000swirc-3.5.5/po/sv/swirc.po000066400000000000000000001772761501213070300154240ustar00rootroot00000000000000# Swedish translations for swirc package # Svenska översättningar för paket swirc. # This file is put in the public domain. # , 2021. # msgid "" msgstr "" "Project-Id-Version: swirc 3.5.5\n" "Report-Msgid-Bugs-To: https://github.com/uhlin/swirc/issues\n" "POT-Creation-Date: 2025-05-17 12:31+0200\n" "PO-Revision-Date: 2025-01-23 06:25+0100\n" "Last-Translator: Markus Uhlin \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../src/commands/colormap.cpp:153 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:222 msgid "Yes" msgstr "Ja" #: ../src/commands/colormap.cpp:154 ../src/commands/dcc.cpp:1010 #: ../src/commands/dcc.cpp:1053 ../src/statusbar.cpp:223 msgid "No" msgstr "Nej" #: ../src/commands/connect.c:211 msgid "Connect using password? [Y/n]: " msgstr "Ansluta med lösenord? [Y/n]: " #: ../src/commands/connect.c:219 ../src/commands/connect.c:280 #: ../src/commands/connect.c:327 ../src/nestHome.c:202 msgid "input too big" msgstr "input för stor" #: ../src/commands/connect.c:248 #, c-format msgid "%s: string copy error" msgstr "%s: strängkopieringsfel" #: ../src/commands/connect.c:264 msgid "Server password (will not echo): " msgstr "Serverlösenord (kommer inte att eka): " #: ../src/commands/connect.c:317 #, c-format msgid "Your choice (0-%d): " msgstr "Ditt val (0-%d): " #: ../src/commands/connect.c:520 #, c-format msgid "Next reconnect attempt in %ld seconds..." msgstr "Nästa återanslutningsförsök om %ld sekunder..." #: ../src/commands/connect.c:532 msgid "Reconnection aborted!" msgstr "Återanslutningen avbröts!" #: ../src/commands/connect.c:572 msgid "Cannot initiate use of the Winsock DLL" msgstr "Kan inte initiera användning av Winsock DLL" #: ../src/commands/connect.c:576 msgid "Use of the Winsock DLL granted" msgstr "Användning av Winsock DLL beviljad" #: ../src/commands/ctcp.c:77 ../src/commands/ftp.cpp:873 #: ../src/commands/ftp.cpp:960 ../src/commands/ftp.cpp:1009 #: ../src/commands/ftp.cpp:1046 ../src/commands/ftp.cpp:1128 msgid "Insufficient arguments" msgstr "Otillräckliga argument" #: ../src/commands/ctcp.c:86 #, c-format msgid "%s: insufficient arguments" msgstr "%s: otillräckliga argument" #: ../src/commands/ctcp.c:89 #, c-format msgid "%s: invalid target" msgstr "%s: ogiltigt mål" #: ../src/commands/ctcp.c:94 #, c-format msgid "%s: forbidden channel name characters" msgstr "%s: förbjudna kanalnamnstecken" #: ../src/commands/ctcp.c:105 msgid "Invalid CTCP query" msgstr "Ogiltig CTCP fråga" #: ../src/commands/dcc.cpp:212 msgid "Write error" msgstr "Skrivfel" #: ../src/commands/dcc.cpp:337 #, c-format msgid "%s: wrote: %s" msgstr "%s: skrev: %s" #: ../src/commands/dcc.cpp:340 #, c-format msgid "%s: did not complete: %s" msgstr "%s: slutfördes inte: %s" #: ../src/commands/dcc.cpp:1017 #, c-format msgid "%sStarted%s: %s" msgstr "%sBörjade%s: %s" #: ../src/commands/dcc.cpp:1019 #, c-format msgid "%sStopped%s: %s" msgstr "%sSlutade%s: %s" #: ../src/commands/dcc.cpp:1393 #, c-format msgid "sending '%s' to %s (%.1f%c)..." msgstr "skickar '%s' till %s (%.1f%c)..." #: ../src/commands/dcc.cpp:1484 msgid "Missing certs. Not starting the DCC server. Please create them." msgstr "Saknar certifikat. Startar inte DCC servern. Vänligen skapa dem." #: ../src/commands/dcc.cpp:1590 #, c-format msgid "%s: added: '%s' (%.1f%c)" msgstr "%s: lade till: '%s' (%.1f%c)" #: ../src/commands/dcc.cpp:1592 #, c-format msgid "%s: from: %s <%s@%s>" msgstr "%s: från: %s <%s@%s>" #: ../src/commands/dcc.cpp:1594 msgid "To get the file, type:" msgstr "För att hämta filen, skriv:" #: ../src/commands/dcc.cpp:1845 ../src/commands/ftp.cpp:805 #, c-format msgid "%s: file read error" msgstr "%s: filläsfel" #: ../src/commands/dcc.cpp:1850 #, c-format msgid "%s: tls write error" msgstr "%s: tls skrivfel" #: ../src/commands/dcc.cpp:1867 #, c-format msgid "started: %s" msgstr "började: %s" #: ../src/commands/dcc.cpp:1868 #, c-format msgid "stopped: %s" msgstr "slutade: %s" #: ../src/commands/dcc.cpp:1885 msgid "read request error" msgstr "läsförfrågan fel" #: ../src/commands/dcc.cpp:1892 msgid "unable to find the send object" msgstr "kan inte hitta sändningsobjektet" #: ../src/commands/dcc.cpp:1900 msgid "already sent file" msgstr "redan skickad fil" #: ../src/commands/dcc.cpp:1903 msgid "the send object is in a locked state" msgstr "sändningsobjektet är i låst tillstånd" #: ../src/commands/dcc.cpp:1908 msgid "file open error" msgstr "filöppningsfel" #: ../src/commands/dcc.cpp:1927 #, c-format msgid "%s: successfully sent file: %s" msgstr "%s: skickad fil: %s" #: ../src/commands/dcc.cpp:1931 #, c-format msgid "%s: file transfer incomplete: %s" msgstr "%s: filöverföringen ofullständig: %s" #: ../src/commands/ftp.cpp:181 msgid "Invalid username" msgstr "Ogiltigt användarnamn" #: ../src/commands/ftp.cpp:183 msgid "Too long password" msgstr "För långt lösenord" #: ../src/commands/ftp.cpp:185 msgid "Invalid hostname" msgstr "Ogiltigt värdnamn" #: ../src/commands/ftp.cpp:187 msgid "Invalid port number" msgstr "Ogiltigt portnummer" #: ../src/commands/ftp.cpp:190 ../src/network.cpp:308 msgid "Unable to get a list of IP addresses" msgstr "Det gick inte att få en lista över IP adresser" #: ../src/commands/ftp.cpp:193 ../src/commands/ftp.cpp:533 #: ../src/network.cpp:175 msgid "Failed to establish a connection" msgstr "Det gick inte att upprätta en anslutning" #: ../src/commands/ftp.cpp:204 ../src/commands/ftp.cpp:886 #: ../src/commands/ftp.cpp:1257 msgid "Cannot send" msgstr "Kan inte skicka" #: ../src/commands/ftp.cpp:521 msgid "Failed to create an endpoint for communication" msgstr "Det gick inte att skapa en slutpunkt för kommunikation" #: ../src/commands/ftp.cpp:813 #, c-format msgid "%s: bytes sent mismatch bytes read" msgstr "%s: bytes skickade matchade inte bytes lästa" #: ../src/commands/ftp.cpp:876 ../src/commands/ftp.cpp:1245 msgid "No control connection" msgstr "Ingen kontrollanslutning" #: ../src/commands/ftp.cpp:879 ../src/commands/ftp.cpp:901 #: ../src/commands/ftp.cpp:1253 msgid "Invalid network socket" msgstr "Ogiltig nätverkssocket" #: ../src/commands/ftp.cpp:963 ../src/commands/ftp.cpp:1049 msgid "Blank file path" msgstr "Tom filsökväg" #: ../src/commands/ftp.cpp:1249 msgid "Already in passive mode" msgstr "Redan i passivt läge" #: ../src/commands/ftp.cpp:1272 msgid "Failed to enter passive mode" msgstr "Det gick inte att gå in i passivt läge" #: ../src/commands/theme.c:313 #, c-format msgid "failed to remove: %s" msgstr "misslyckades med att ta bort: %s" #: ../src/commands/theme.c:333 msgid "no such theme in the database" msgstr "inget sådant tema i databasen" #: ../src/commands/theme.c:353 msgid "theme installed (use 'set' to activate it)" msgstr "tema installerat (använd 'set' för att aktivera det)" #: ../src/commands/theme.c:356 msgid "failed to install the theme :-(" msgstr "misslyckades med att installera temat :-(" #: ../src/commands/theme.c:404 msgid "non-existent" msgstr "obefintlig" #: ../src/commands/theme.c:417 msgid "theme activated" msgstr "tema aktiverat" #: ../src/events/account.cpp:56 #, c-format msgid "%s%s%c %s%s@%s%s has logged into a new account %s%s%c" msgstr "%s%s%c %s%s@%s%s har loggat in på ett nytt konto %s%s%c" #: ../src/events/account.cpp:69 #, c-format msgid "%s%s%c %s%s@%s%s has logged out of their account" msgstr "%s%s%c %s%s@%s%s har loggat ut från sitt konto" #: ../src/events/away.cpp:55 #, c-format msgid "%s%s%c %s%s@%s%s is no longer marked as being away!" msgstr "%s%s%c %s%s@%s%s är inte längre markerad som borta!" #: ../src/events/away.cpp:68 #, c-format msgid "%s%s%c %s%s@%s%s has been marked as being away (%s)" msgstr "%s%s%c %s%s@%s%s har markerats som borta (%s)" #: ../src/events/channel.cpp:100 #, c-format msgid "Homepage for %s%s%s%c%s: %s" msgstr "Hemsida för %s%s%s%c%s: %s" #: ../src/events/channel.cpp:223 #, c-format msgid "%s%s%c %s%s@%s%s has joined %s%s%c" msgstr "%s%s%c %s%s@%s%s har gått med i %s%s%c" #: ../src/events/channel.cpp:308 #, c-format msgid "%s was kicked from %s%s%c by %s%s%c %s%s%s" msgstr "%s sparkades från %s%s%c av %s%s%c %s%s%s" #. #. * User mode #. #: ../src/events/channel.cpp:580 #, c-format msgid "Mode change %s%s%s for user %c%s%c" msgstr "Lägesändring %s%s%s för användaren %c%s%c" #: ../src/events/channel.cpp:588 #, c-format msgid "mode/%s%s%s%c%s %s%s%s by %s%s%c" msgstr "läge/%s%s%s%c%s %s%s%s av %s%s%c" #: ../src/events/channel.cpp:703 #, c-format msgid "%s%s%c is now known as %s %s%s%c" msgstr "%s%s%c har bytt namn till %s %s%s%c" #: ../src/events/channel.cpp:785 #, c-format msgid "%s%s%c %s%s@%s%s has left %s%s%c %s%s%s" msgstr "%s%s%c %s%s@%s%s har lämnat %s%s%c %s%s%s" #: ../src/events/channel.cpp:916 #, c-format msgid "%s%s%c %s%s@%s%s has quit %s%s%s" msgstr "%s%s%c %s%s@%s%s har avslutat %s%s%s" #: ../src/events/channel.cpp:1011 #, c-format msgid "Topic for %s%s%s%c%s: %s" msgstr "Ämne för %s%s%s%c%s: %s" #: ../src/events/channel.cpp:1071 #, c-format msgid "%c%s%c changed the topic of %c%s%c to: %s" msgstr "%c%s%c ändrade ämnet för %c%s%c till: %s" #: ../src/events/channel.cpp:1149 #, c-format msgid "Topic set by %c%s%c %s%s@%s%s %s%s%s" msgstr "Ämnet satt av %c%s%c %s%s@%s%s %s%s%s" #: ../src/events/channel.cpp:1155 #, c-format msgid "Topic set by %c%s%c %s%s%s" msgstr "Ämnet satt av %c%s%c %s%s%s" #: ../src/events/chghost.cpp:61 #, c-format msgid "%s%s%s has changed their hostname to %s%s@%s%s" msgstr "%s%s%s har ändrat sitt värdnamn till %s%s@%s%s" #: ../src/events/invite.cpp:56 msgid "The invitation has been passed onto the end user" msgstr "Inbjudan har skickats till slutanvändaren" #: ../src/events/invite.cpp:103 #, c-format msgid "%c%s%c %s%s@%s%s invites you to %c%s%c" msgstr "%c%s%c %s%s@%s%s bjuder in dig till %c%s%c" #. #. * TODO: Write to the channels where the user #. * that's doing the invite is in #. #: ../src/events/invite.cpp:113 #, c-format msgid "%c%s%c %s%s@%s%s invites %c%s%c to %c%s%c" msgstr "%c%s%c %s%s@%s%s bjuder in %c%s%c till %c%s%c" #: ../src/events/misc.cpp:246 #, c-format msgid "Channel %s%s%s%c%s created %s" msgstr "Kanalen %s%s%s%c%s skapades %s" #: ../src/events/misc.cpp:303 #, c-format msgid "mode/%s%s%s%c%s %s%s%s" msgstr "läge/%s%s%s%c%s %s%s%s" #: ../src/events/misc.cpp:354 #, c-format msgid "Channel forwarding from %c%s%c to %c%s%c" msgstr "Kanalvidarebefordran från %c%s%c till %c%s%c" #: ../src/events/misc.cpp:431 #, c-format msgid "Nickname %c%s%c is already in use" msgstr "Smeknamnet %c%s%c används redan" #: ../src/events/misc.cpp:439 msgid "Alternative nickname already tested. Disconnecting..." msgstr "Alternativt smeknamn har redan testats. Kopplar från..." #: ../src/events/misc.cpp:444 #, c-format msgid "Attempting to use alt_nick (%s) instead..." msgstr "Försöker använda alt_nick (%s) istället..." #: ../src/events/misc.cpp:452 msgid "Disconnecting..." msgstr "Kopplar från..." #: ../src/events/misc.cpp:513 msgid "You're now an IRC operator!" msgstr "Du är nu en IRC operatör!" #: ../src/include/commandhelp.h:44 msgid "usage: /admin [target]" msgstr "användning: /admin [mål]" #: ../src/include/commandhelp.h:46 msgid "" "The admin command is used to find information about the administrator\n" "of the given server, or current server if 'target' parameter is omit-\n" "ted." msgstr "" "Admin-kommandot används för att hitta information om administratören\n" "för den givna servern, eller aktuell server om 'mål'-parametern ute-\n" "lämnas." #: ../src/include/commandhelp.h:53 ../src/include/commandhelp.h:201 #: ../src/include/commandhelp.h:282 msgid "usage:" msgstr "användning:" #: ../src/include/commandhelp.h:60 msgid "Send announcements on IRC." msgstr "Skicka tillkännagivanden på IRC." #: ../src/include/commandhelp.h:65 msgid "usage: /away [reason]" msgstr "användning: /away [anledning]" #: ../src/include/commandhelp.h:67 msgid "" "Marks yourself as being away (with a reason). If the reason is omitted\n" "you will be marked as no longer being away." msgstr "" "Markerar dig själv som borta (med anledning). Om orsaken utelämnas\n" "kommer du att markeras som att du inte längre är borta." #: ../src/include/commandhelp.h:73 msgid "usage: /ban " msgstr "användning: /ban " #: ../src/include/commandhelp.h:75 msgid "" "Sets a channel ban which rejects all users whose 'nick!user@host'\n" "matches the provided mask from joining the channel. Wildcards are okay\n" "and the active window must be an IRC channel." msgstr "" "Ställer in ett kanalförbud som avvisar alla användare vars\n" "'nick!user@host' matchar den angivna masken från att gå med i\n" "kanalen. Jokertecken är okej och det aktiva fönstret måste vara en\n" "IRC kanal." #: ../src/include/commandhelp.h:82 msgid "usage: /banlist [channel]" msgstr "användning: /banlist [kanal]" #: ../src/include/commandhelp.h:84 msgid "" "Outputs a channel's banlist. If the channel argument is left empty and\n" "the active window is an IRC channel, Swirc will output the banlist for\n" "that channel." msgstr "" "Matar ut en kanals bannlista. Om kanalargumentet lämnas tomt och det\n" "aktiva fönstret är en IRC kanal, kommer Swirc att mata ut bannlistan\n" "för den kanalen." #: ../src/include/commandhelp.h:91 msgid "usage: /beep " msgstr "användning: /beep " #: ../src/include/commandhelp.h:93 msgid "Send beeps. (ICB only)" msgstr "Skicka pip. (Endast ICB)" #: ../src/include/commandhelp.h:98 msgid "usage: /boot " msgstr "användning: /boot " #: ../src/include/commandhelp.h:100 msgid "Kick a user out of your group. (ICB only)" msgstr "Kasta ut en användare från din grupp. (Endast ICB)" #: ../src/include/commandhelp.h:105 msgid "usage: /chanserv <[service hostname | --]> [...]" msgstr "användning: /chanserv <[tjänstens värdnamn | --]> [...]" #: ../src/include/commandhelp.h:107 msgid "" "Communicate with your IRC network's channel service. If the initial\n" "argument equals to '--', then the value of setting 'chanserv_host' is\n" "used as a service hostname." msgstr "" "Kommunicera med ditt IRC nätverks kanaltjänst. Om det initiala\n" "argumentet är lika med '--' används värdet för inställningen\n" "'chanserv_host' som ett tjänstvärdnamn." #: ../src/include/commandhelp.h:118 msgid "usage: /ctcp " msgstr "användning: /ctcp " #: ../src/include/commandhelp.h:120 msgid "" "Send a CTCP query to 'target', which is either a nickname or an IRC\n" "channel." msgstr "" "Skicka en CTCP fråga till 'target', som antingen är ett smeknamn eller\n" "en IRC kanal." #: ../src/include/commandhelp.h:123 msgid "Query can be either:" msgstr "Fråga kan vara antingen:" #: ../src/include/commandhelp.h:131 msgid "usage: /cap [ls | list]" msgstr "användning: /cap [ls | list]" #: ../src/include/commandhelp.h:133 msgid "" "Lists the (IRCv3) capabilities supported by the server and/or the\n" "capabilities associated with the active connection." msgstr "" "Listar (IRCv3) funktioner som stöds av servern och/eller funktioner\n" "som är associerade med den aktiva anslutningen." #: ../src/include/commandhelp.h:139 msgid "usage: /cleartoasts" msgstr "användning: /cleartoasts" #: ../src/include/commandhelp.h:141 msgid "" "On Windows Swirc sends toast notifications. By running this command\n" "all notifications associated with Swirc will be cleared." msgstr "" "På Windows skickar Swirc toastaviseringar. Genom att köra det här\n" "kommandot kommer alla meddelanden associerade med Swirc att raderas." #: ../src/include/commandhelp.h:147 msgid "usage: /close" msgstr "användning: /close" #: ../src/include/commandhelp.h:149 msgid "" "Closes the active window. It's not possible to close the status\n" "window. And while connected it's not possible to close a channel, in\n" "that case instead use '/part'." msgstr "" "Stänger det aktiva fönstret. Det går inte att stänga\n" "statusfönstret. Och när du är ansluten är det inte möjligt att stänga\n" "en kanal, använd i så fall istället '/part'." #: ../src/include/commandhelp.h:156 msgid "usage: /colormap" msgstr "användning: /colormap" #: ../src/include/commandhelp.h:158 msgid "Outputs information about colors." msgstr "Matar ut information om färger." #: ../src/include/commandhelp.h:163 msgid "usage: /connect [-tls] " msgstr "användning: /connect [-tls] " #: ../src/include/commandhelp.h:165 msgid "Connect to given server." msgstr "Anslut till given server." #: ../src/include/commandhelp.h:167 msgid "" "If the port is omitted port 6667 will be chosen. And if the port is\n" "7326 ICB mode is turned on automatically. Further, if the port is 6697\n" "Swirc attempts to initiate a TLS/SSL connection, as well as if '-tls'\n" "is entered." msgstr "" "Om porten utelämnas kommer port 6667 att väljas. Och om porten är 7326\n" "slås ICB läget på automatiskt. Vidare, om porten är 6697 försöker\n" "Swirc initiera en TLS/SSL-anslutning, samt om '-tls' anges." #: ../src/include/commandhelp.h:172 msgid "" "It is possible to connect to a certain IRC network by only entering\n" "the network name. For example: '/connect -tls libera', will connect to\n" "Libera Chat using an encrypted connection. Preprogrammed network names\n" "are:" msgstr "" "Det är möjligt att ansluta till ett visst IRC nätverk genom att endast\n" "ange nätverksnamnet. Till exempel: '/connect -tls libera', kommer att\n" "ansluta till Libera Chat med en krypterad anslutning. Förprogrammerade\n" "nätverksnamn är:" #: ../src/include/commandhelp.h:192 msgid "usage: /cycle [channel]" msgstr "användning: /cycle [kanal]" #: ../src/include/commandhelp.h:194 msgid "" "Cycle a channel, i.e. '/part' plus '/join'. If the channel argument is\n" "omitted and the active window is an IRC channel, the client will cycle\n" "that channel." msgstr "" "Cykla en kanal, d.v.s. '/part' plus '/join'. Om kanalargumentet\n" "utelämnas och det aktiva fönstret är en IRC kanal, kommer klienten att\n" "cykla den kanalen." #: ../src/include/commandhelp.h:209 msgid "" "Get and send files. Swirc implements its own variant of DCC meaning\n" "it's incompatible with other IRC clients. Transport Layer Security is\n" "forced and for now the DCC feature isn't available in ICB mode." msgstr "" "Hämta och skicka filer. Swirc implementerar sin egen variant av DCC\n" "vilket betyder att den är inkompatibel med andra IRC klienter.\n" "Transport Layer Security är framtvingat och för närvarande är DCC\n" "funktionen inte tillgänglig i ICB-läge." #: ../src/include/commandhelp.h:216 msgid "usage: /deop " msgstr "användning: /deop " #: ../src/include/commandhelp.h:218 msgid "Remove the channel operator privilege from another user." msgstr "Ta bort kanaloperatörsprivilegiet från en annan användare." #: ../src/include/commandhelp.h:223 msgid "usage: /devoice " msgstr "användning: /devoice " #: ../src/include/commandhelp.h:225 msgid "Remove the channel voice privilege from another user." msgstr "Ta bort kanalröstprivilegiet från en annan användare." #: ../src/include/commandhelp.h:230 msgid "usage: /die [--I-am-sure]" msgstr "användning: /die [--I-am-sure]" #: ../src/include/commandhelp.h:232 msgid "" "An IRC operator can use this command to shutdown the server. Please\n" "confirm that this is what you really want by typing '--I-am-sure'." msgstr "" "En IRC operatör kan använda detta kommando för att stänga av\n" "servern. Vänligen bekräfta att det är vad du verkligen vill genom att\n" "skriva '--I-am-sure'." #: ../src/include/commandhelp.h:238 msgid "usage: /disconnect [message]" msgstr "användning: /disconnect [meddelande]" #: ../src/include/commandhelp.h:240 msgid "" "Disconnect from IRC, but don't quit the program. A disconnect message\n" "is optional." msgstr "" "Koppla från IRC, men avsluta inte programmet. Ett frånkopplings-\n" "meddelande är valfritt." #: ../src/include/commandhelp.h:246 msgid "usage: /echo " msgstr "användning: /echo " #: ../src/include/commandhelp.h:248 msgid "Writes text to the current window without sending anything." msgstr "Skriver text till det aktuella fönstret utan att skicka något." #: ../src/include/commandhelp.h:252 msgid "Echo 'Hello World':" msgstr "Echo 'Hej Världen':" #: ../src/include/commandhelp.h:253 msgid " /echo Hello World" msgstr " /echo Hej Världen" #: ../src/include/commandhelp.h:258 msgid "usage: /exlist [channel]" msgstr "användning: /exlist [kanal]" #: ../src/include/commandhelp.h:260 msgid "" "Outputs a channel's exception list. An exception mask (+e) overrides a\n" "ban mask. If the channel argument is omitted and the active window is\n" "an IRC channel, the client will output the exception list for that\n" "channel." msgstr "" "Matar ut en kanals undantagslista. En undantagsmask (+e) åsidosätter\n" "en förbudsmask. Om kanalargumentet utelämnas och det aktiva fönstret\n" "är en IRC kanal, kommer klienten att mata ut undantagslistan för den\n" "kanalen." #: ../src/include/commandhelp.h:268 msgid "usage: /fetchdic [name]" msgstr "användning: /fetchdic [namn]" #: ../src/include/commandhelp.h:270 msgid "" "Fetches spelling dictionaries. If the name argument is omitted Swirc\n" "will output a list of available dictionaries. The list is obtained\n" "remotely which means its contents can be updated at any time." msgstr "" "Hämtar stavningsordböcker. Om namnargumentet utelämnas kommer Swirc\n" "att mata ut en lista över tillgängliga ordböcker. Listan erhålls på\n" "distans vilket innebär att dess innehåll kan uppdateras när som helst." #: ../src/include/commandhelp.h:276 msgid "Fetch the american english dictionary:" msgstr "Hämta den amerikansk engelska ordboken:" #: ../src/include/commandhelp.h:295 msgid "" "Retrieve and store files on a FTP server. (The communication is done\n" "in clear text, i.e. unencrypted.)" msgstr "" "Hämta och lagra filer på en FTP server. (Kommunikationen sker i\n" "klartext, dvs okrypterad.)" #: ../src/include/commandhelp.h:301 msgid "usage: /gline [ :]" msgstr "användning: /gline [ :]" #: ../src/include/commandhelp.h:303 msgid "Network-wide bans." msgstr "Nätverksomfattande förbud." #: ../src/include/commandhelp.h:305 msgid "" "When a client matches a G-line it cannot connect to ANY server on the\n" "IRC network for 'duration' seconds. If the duration is zero then the\n" "G-line will be permanent." msgstr "" "När en klient matchar en G-linje kan den inte ansluta till NÅGON\n" "server på IRC nätverket under 'varaktighet' sekunder. Om varaktigheten\n" "är noll kommer G-linjen att vara permanent." #: ../src/include/commandhelp.h:309 msgid "(If no duration and no reason is given the G-line is deleted.)" msgstr "(Om ingen varaktighet och ingen anledning anges raderas G-linjen.)" #: ../src/include/commandhelp.h:318 msgid "usage: /group " msgstr "användning: /group " #: ../src/include/commandhelp.h:320 msgid "Changes ICB group." msgstr "Byter ICB grupp." #: ../src/include/commandhelp.h:325 msgid "usage: /help [command]" msgstr "användning: /help [kommando]" #: ../src/include/commandhelp.h:327 msgid "Outputs a list of all available commands." msgstr "Matar ut en lista över alla tillgängliga kommandon." #: ../src/include/commandhelp.h:328 msgid "(Or help for a specific command.)" msgstr "(Eller hjälp för ett specifikt kommando.)" #: ../src/include/commandhelp.h:333 msgid "usage: /ignore [regex]" msgstr "användning: /ignore [reguljärt uttryck]" #: ../src/include/commandhelp.h:335 msgid "" "Ignores all 'nick!user@host' that matches the given regular\n" "expression, this by using the POSIX basic regular expression\n" "grammar. This command isn't to be used by beginners and I advice you\n" "to be careful when using it. I highly recommend the use of:" msgstr "" "Ignorerar alla 'nick!user@host' som matchar det givna reguljära\n" "uttrycket, detta genom att använda POSIXs grundläggande reguljära\n" "uttrycksgrammatik. Det här kommandot ska inte användas av nybörjare\n" "och jag råder dig att vara försiktig när du använder det.\n" "Jag rekommenderar starkt användningen av:" #: ../src/include/commandhelp.h:340 msgid " 1. ^ Matches the starting position within the string, if it is" msgstr " 1. ^ Matchar startpositionen inom strängen, om det är" #: ../src/include/commandhelp.h:341 msgid " the first character of the regular expression." msgstr " det första tecknet i det reguljära uttrycket." #: ../src/include/commandhelp.h:342 msgid " 2. $ Matches the ending position of the string, if it is the last" msgstr " 2. $ Matchar strängens slutposition, om det är det sista" #: ../src/include/commandhelp.h:343 msgid " character of the regular expression." msgstr " tecknet i det reguljära uttrycket." #: ../src/include/commandhelp.h:347 msgid "Ignore nickname 'troll':" msgstr "Ignorera smeknamnet 'troll':" #: ../src/include/commandhelp.h:350 msgid "Ignore all users with username 'troll':" msgstr "Ignorera alla användare med användarnamnet 'troll':" #: ../src/include/commandhelp.h:353 msgid "Ignore all users with hostname 'insecure.org':" msgstr "Ignorera alla användare med värdnamnet 'insecure.org':" #: ../src/include/commandhelp.h:356 msgid "Ignore all users with a Chinese domain (.cn):" msgstr "Ignorera alla användare med en kinesisk domän (.cn):" #: ../src/include/commandhelp.h:362 msgid "usage: /ilist [channel]" msgstr "användning: /ilist [kanal]" #: ../src/include/commandhelp.h:364 msgid "" "Outputs a channel's invitation list. An invitation mask (+I) overrides\n" "the invite-only flag (+i). If the channel argument is omitted and the\n" "active window is an IRC channel, the client will output the invitation\n" "list for that channel." msgstr "" "Matar ut en kanals inbjudningslista. En inbjudningsmask (+I)\n" "åsidosätter flaggan endast för inbjudan (+i). Om kanalargumentet\n" "utelämnas och det aktiva fönstret är en IRC kanal, kommer klienten att\n" "mata ut inbjudningslistan för den kanalen." #: ../src/include/commandhelp.h:372 msgid "usage: /info [target]" msgstr "användning: /info [mål]" #: ../src/include/commandhelp.h:374 msgid "The info command returns information about the server." msgstr "Kommandot info returnerar information om servern." #: ../src/include/commandhelp.h:379 msgid "usage: /invite " msgstr "användning: /invite " #: ../src/include/commandhelp.h:381 msgid "Invites 'targ_nick' to a channel." msgstr "Bjuder in 'smeknamn' till en kanal." #: ../src/include/commandhelp.h:386 msgid "usage: /ison [nick2] [nick3] [...]" msgstr "användning: /ison [nick2] [nick3] [...]" #: ../src/include/commandhelp.h:388 msgid "Checks whether users are on IRC." msgstr "Kontrollerar om användare är på IRC." #: ../src/include/commandhelp.h:393 msgid "usage: /join [key]" msgstr "användning: /join [nyckel]" #: ../src/include/commandhelp.h:395 msgid "Joins a channel (optionally by using a key)." msgstr "Går med i en kanal (valfritt genom att använda en nyckel)." #: ../src/include/commandhelp.h:399 msgid "Join a channel with name 'libera':" msgstr "Gå med i en kanal med namnet 'libera':" #: ../src/include/commandhelp.h:402 msgid "Join a key-protected channel:" msgstr "Gå med i en nyckelskyddad kanal:" #: ../src/include/commandhelp.h:408 msgid "usage: /kick [reason]" msgstr "användning: /kick [anledning]" #: ../src/include/commandhelp.h:410 msgid "" "Kicks one or more users out of a channel. The users are given in a\n" "comma-separated list. A reason is optional and the active window must\n" "be an IRC channel." msgstr "" "Sparkar ut en eller flera användare från en kanal. Användarna anges i\n" "en kommaseparerad lista. En anledning är valfri och det aktiva\n" "fönstret måste vara en IRC kanal." #: ../src/include/commandhelp.h:417 msgid "usage: /kickban [reason]" msgstr "användning: /kickban [anledning]" #: ../src/include/commandhelp.h:419 msgid "" "Set a channel ban with given 'mask' and kick the user 'nick' out of a\n" "channel. A reason is optional and the active window must be an IRC\n" "channel." msgstr "" "Ställ in ett kanalförbud med given 'mask' och sparka ut användaren\n" "'nick' från en kanal. En anledning är valfri och det aktiva fönstret\n" "måste vara en IRC kanal." #: ../src/include/commandhelp.h:426 msgid "usage: /kill " msgstr "användning: /kill " #: ../src/include/commandhelp.h:428 msgid "Disconnect a user from the connected network." msgstr "Koppla bort en användare från det anslutna nätverket." #: ../src/include/commandhelp.h:429 msgid "(Requires IRC op privilege.)" msgstr "(Kräver IRC op-behörighet.)" #: ../src/include/commandhelp.h:434 msgid "usage: /kline [ :]" msgstr "användning: /kline [ :]" #: ../src/include/commandhelp.h:436 msgid "Server-local bans." msgstr "Serverlokala förbud." #: ../src/include/commandhelp.h:438 msgid "" "When a client matches a K-line it cannot connect to the local server\n" "for 'duration' seconds. If the duration is zero then the K-line will\n" "be permanent." msgstr "" "När en klient matchar en K-linje kan den inte ansluta till den lokala\n" "servern under 'varaktighet' sekunder. Om varaktigheten är noll kommer\n" "K-linjen att vara permanent." #: ../src/include/commandhelp.h:442 msgid "(If no duration and no reason is given the K-line is deleted.)" msgstr "(Om ingen varaktighet och ingen anledning anges tas K-linjen bort.)" #: ../src/include/commandhelp.h:451 msgid "usage: /list [min_users][,pattern][...]]" msgstr "användning: /list [min_users][,mönster][...]]" #: ../src/include/commandhelp.h:453 msgid "" "Lists channels and their topics. Without any arguments the list is\n" "HUGE. For example, '/list >1500' will only list channels that have\n" "more than 1500 users." msgstr "" "Listar kanaler och deras ämnen. Utan några argument är listan\n" "ENORM. Till exempel kommer '/list >1500' bara att lista kanaler som\n" "har fler än 1500 användare." #: ../src/include/commandhelp.h:457 msgid "" "Depending on the IRC server software used by your network the usage\n" "may differ." msgstr "" "Beroende på vilken IRC serverprogramvara som används av ditt nätverk\n" "kan användningen variera." #: ../src/include/commandhelp.h:463 msgid "usage: /me " msgstr "användning: /me " #: ../src/include/commandhelp.h:465 msgid "Send an 'action' message. (Used to simulate role playing on IRC.)" msgstr "" "Skicka ett 'action'-meddelande. (Används för att simulera rollspel på IRC.)" #: ../src/include/commandhelp.h:470 msgid "usage: /mode [...]" msgstr "användning: /mode [...]" #: ../src/include/commandhelp.h:472 msgid "Alter modes." msgstr "Ändra lägen." #: ../src/include/commandhelp.h:476 msgid " o - give/take the channel operator privilege" msgstr " o - ge/ta kanaloperatörsprivilegiet" #: ../src/include/commandhelp.h:477 msgid " v - give/take the channel voice privilege" msgstr " v - ge/ta kanal-röst-privilegiet" #: ../src/include/commandhelp.h:479 msgid " i - invite-only channel" msgstr " i - invite-only kanal" #: ../src/include/commandhelp.h:480 msgid " m - moderated channel" msgstr " m - modererad kanal" #: ../src/include/commandhelp.h:481 msgid " n - no messages to a channel from clients on the outside" msgstr " n - inga meddelanden till en kanal från klienter på utsidan" #: ../src/include/commandhelp.h:482 msgid " p - private channel" msgstr " p - privat kanal" #: ../src/include/commandhelp.h:483 msgid " s - secret channel" msgstr " s - hemlig kanal" #: ../src/include/commandhelp.h:484 msgid " t - topic settable by channel operators only" msgstr " t - ämne kan endast ställas in av kanaloperatörer" #: ../src/include/commandhelp.h:486 msgid " k - set/remove the channel key (password)" msgstr " k - ställ in/ta bort kanalnyckeln (lösenordet)" #: ../src/include/commandhelp.h:487 msgid " l - set/remove the channel user limit" msgstr " l - ställ in/ta bort kanalanvändargränsen" #: ../src/include/commandhelp.h:489 msgid " b - set/remove a ban mask" msgstr " b - ställ in/ta bort en förbudsmask" #: ../src/include/commandhelp.h:490 msgid " e - set/remove an exception mask to override a ban mask" msgstr "" " e - ställ in/ta bort en undantagsmask för att åsidosätta en förbudsmask" #: ../src/include/commandhelp.h:491 msgid " I - set/remove an invitation mask to override the invite-only flag" msgstr "" " I - ställ in/ta bort en inbjudningsmask för att åsidosätta flaggan " "endast för inbjudan" #: ../src/include/commandhelp.h:495 msgid " i - marks a user as invisible" msgstr " i - markerar en användare som osynlig" #: ../src/include/commandhelp.h:496 msgid " w - the user receives wallops messages" msgstr " w - användaren får wallops-meddelanden" #: ../src/include/commandhelp.h:500 msgid " Give channel operator privilege to 'Companion' in #foo:" msgstr " Ge kanaloperatörsprivilegium till 'Companion' i #foo:" #: ../src/include/commandhelp.h:503 msgid " Restrict messaging to channel #linux:" msgstr " Begränsa meddelanden till kanal #linux:" #: ../src/include/commandhelp.h:506 msgid " Limit user count for #freenode to 10:" msgstr " Begränsa antalet användare för #freenode till 10:" #: ../src/include/commandhelp.h:509 msgid " Deny all users with hostname spammers.net from joining #chatzone:" msgstr "" " Neka alla användare med värdnamnet spammers.net från att gå med i " "#chatzone:" #: ../src/include/commandhelp.h:512 msgid " Turn on reception of WALLOPS messages:" msgstr " Slå på mottagning av WALLOPS meddelanden:" #: ../src/include/commandhelp.h:518 msgid "usage: /msg " msgstr "användning: /msg " #: ../src/include/commandhelp.h:520 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels." msgstr "" "Används för att skicka privata meddelanden mellan användare, samt för\n" "att skicka meddelanden till kanaler." #: ../src/include/commandhelp.h:526 msgid "usage: /nick " msgstr "användning: /nick " #: ../src/include/commandhelp.h:528 msgid "Sets your nickname." msgstr "Ställer in ditt smeknamn." #: ../src/include/commandhelp.h:533 msgid "usage: /nickserv <[service hostname | --]> [...]" msgstr "användning: /nickserv <[tjänstens värdnamn | --]> [...]" #: ../src/include/commandhelp.h:535 msgid "Communicate with your IRC network's nickname service." msgstr "Kommunicera med ditt IRC nätverks smeknamnstjänst." #: ../src/include/commandhelp.h:537 msgid "If the initial argument equals to '--' then the:" msgstr "Om det initiala argumentet är lika med '--' så kommer:" #: ../src/include/commandhelp.h:539 msgid " 1) Value of setting 'nickserv_host' will be used as service" msgstr "" " 1) Värdet för inställningen 'nickserv_host' att användas som tjänstens" #: ../src/include/commandhelp.h:540 msgid " hostname." msgstr " värdnamn." #: ../src/include/commandhelp.h:541 msgid " 2) Command call won't be added to the command history provided" msgstr "" " 2) Kommandoanropet inte att läggas till kommandohistoriken förutsatt" #: ../src/include/commandhelp.h:542 msgid " that the second argument is 'identify'." msgstr " att det andra argumentet är 'identify'." #: ../src/include/commandhelp.h:544 msgid "" "The correct service hostname is not always the same as the visible\n" "hostname of NickServ. FYI at the AnonOps IRC network the visible\n" "hostname of NickServ is anonops.in (when this text was written)\n" "but you should use 'services.anonops.com'. As a fallback:" msgstr "" "Det korrekta värdnamnet för tjänsten är inte alltid detsamma som det\n" "synliga värdnamnet för NickServ. För din kännedom, på AnonOps\n" "IRC-nätverket är det synliga värdnamnet för NickServ anonops.in\n" "(när denna text skrevs) men du bör använda 'services.anonops.com'.\n" "Som en reserv:" #: ../src/include/commandhelp.h:548 msgid "" "'/query NickServ' can be used in order to communicate with the\n" "service." msgstr "'/query NickServ' kan användas för att kommunicera med tjänsten." #: ../src/include/commandhelp.h:554 msgid "usage: /notice " msgstr "användning: /notice " #: ../src/include/commandhelp.h:556 msgid "" "Used to send private messages between users, as well as to send\n" "messages to channels. (In notice form.)" msgstr "" "Används för att skicka privata meddelanden mellan användare, samt för\n" "att skicka meddelanden till kanaler. (I notisform.)" #: ../src/include/commandhelp.h:562 msgid "usage: /op " msgstr "användning: /op " #: ../src/include/commandhelp.h:564 msgid "Gives the channel operator privilege to another user." msgstr "Ger kanaloperatörsprivilegiet till en annan användare." #: ../src/include/commandhelp.h:569 msgid "usage: /oper " msgstr "användning: /oper " #: ../src/include/commandhelp.h:571 msgid "Identifies yourself as an IRC operator." msgstr "Identifierar dig själv som IRC operatör." #: ../src/include/commandhelp.h:576 msgid "usage: /part [channel] [message]" msgstr "användning: /part [kanal] [meddelande]" #: ../src/include/commandhelp.h:578 msgid "" "Leaves a channel (optionally with a message). If the command is called\n" "without any arguments and the current window is an IRC channel, that\n" "channel will be the target." msgstr "" "Lämnar en kanal (valfritt med ett meddelande). Om kommandot anropas\n" "utan några argument och det aktuella fönstret är en IRC kanal, kommer\n" "den kanalen att vara målet." #: ../src/include/commandhelp.h:584 msgid " Leave channel #chatzone with message 'bye':" msgstr " Lämna kanal #chatzone med meddelandet 'Hej då':" #: ../src/include/commandhelp.h:585 msgid " /part #chatzone bye" msgstr " /part #chatzone Hej då" #: ../src/include/commandhelp.h:590 msgid "usage: /passmod " msgstr "användning: /passmod " #: ../src/include/commandhelp.h:592 msgid "Pass the ICB moderation privilege to another group member." msgstr "Överlämna ICB modereringsprivilegiet till en annan gruppmedlem." #: ../src/include/commandhelp.h:597 msgid "usage: /qbot <[service hostname | --]> [...]" msgstr "användning: /qbot <[tjänstens värdnamn | --]> [...]" #: ../src/include/commandhelp.h:599 msgid "" "Communicate with QuakeNet's network service, the Q bot. If the initial\n" "argument equals to '--', then the value of setting 'qbot_host' is used\n" "as a service hostname." msgstr "" "Kommunicera med QuakeNets nätverkstjänst, Q-boten. Om det initiala\n" "argumentet är lika med '--' används värdet för inställningen\n" "'qbot_host' som ett tjänstvärdnamn." #: ../src/include/commandhelp.h:606 msgid "usage: /query [nick]" msgstr "användning: /query [nick]" #: ../src/include/commandhelp.h:608 msgid "" "Starts a private conversation with 'nick'. If 'nick' is omitted and\n" "the active window is a private conversation, the action will be to\n" "close it." msgstr "" "Startar en privat konversation med 'nick'. Om 'nick' utelämnas och det\n" "aktiva fönstret är en privat konversation, kommer åtgärden att vara\n" "att stänga den." #: ../src/include/commandhelp.h:615 msgid "usage: /quit [message]" msgstr "användning: /quit [meddelande]" #: ../src/include/commandhelp.h:617 msgid "" "Disconnect from IRC and quit the program. A disconnect message is\n" "optional." msgstr "" "Koppla från IRC och avsluta programmet. Ett frånkopplingsmeddelande är\n" "valfritt." #: ../src/include/commandhelp.h:623 msgid "usage: /rehash" msgstr "användning: /rehash" #: ../src/include/commandhelp.h:625 msgid "" "The rehash command is an administrative command which can be used by\n" "an IRC operator to force the server to re-read and process its conf-\n" "iguration file." msgstr "" "Rehash-kommandot är ett administrativt kommando som kan användas av en\n" "IRC operatör för att tvinga servern att läsa om och bearbeta sin konf-\n" "igurationsfil." #: ../src/include/commandhelp.h:632 msgid "usage: /resize" msgstr "användning: /resize" #: ../src/include/commandhelp.h:634 msgid "" "Resize the terminal. For example, Windows doesn't send 'SIGWINCH',\n" "instead this command can be used. First resize the window then run\n" "this command." msgstr "" "Ändra storlek på terminalen. Till exempel, Windows skickar inte\n" "'SIGWINCH', istället kan detta kommando användas. Ändra först\n" "storleken på fönstret och kör sedan det här kommandot." #: ../src/include/commandhelp.h:641 msgid "usage: /restart [--I-am-sure]" msgstr "användning: /restart [--I-am-sure]" #: ../src/include/commandhelp.h:643 msgid "" "An IRC operator can use the 'restart'-command to force the server to\n" "restart itself." msgstr "" "En IRC operatör kan använda kommandot 'restart' för att tvinga servern\n" "att starta om sig själv." #: ../src/include/commandhelp.h:649 msgid "usage: /rules" msgstr "användning: /rules" #: ../src/include/commandhelp.h:651 msgid "" "Outputs network/server rules. Not all IRC server software supports\n" "this command. (It's actually quite rare.)" msgstr "" "Matar ut nätverks-/serverregler. Inte all IRC serverprogramvara\n" "stödjer detta kommando. (Det är faktiskt ganska sällsynt.)" #: ../src/include/commandhelp.h:657 msgid "usage: /sasl [...]" msgstr "användning: /sasl [...]" #: ../src/include/commandhelp.h:659 msgid "Simple Authentication and Security Layer." msgstr "Enkelt autentiserings- och säkerhetslager. (SASL)" #: ../src/include/commandhelp.h:660 msgid "Operation can be either:" msgstr "Operation kan vara antingen:" #: ../src/include/commandhelp.h:672 msgid "" "SASL is a method that lets you identify with NickServ during the\n" "connection process eliminating the need to do it manually." msgstr "" "SASL är en metod som låter dig identifiera dig med NickServ under\n" "anslutningsprocessen, vilket eliminerar behovet av att göra det\n" "manuellt." #: ../src/include/commandhelp.h:674 msgid "(To use SASL you must register your nickname.)" msgstr "(För att använda SASL måste du registrera ditt smeknamn.)" #: ../src/include/commandhelp.h:678 msgid " Identification using mechanism ecdsa-nist256p-challenge:" msgstr " Identifiering med mekanism ecdsa-nist256p-challenge:" #: ../src/include/commandhelp.h:686 msgid "" " (The only IRC network that I know of that is supporting this\n" " mechanism is Libera Chat.)" msgstr "" " (Det enda IRC nätverket som jag känner till som stödjer denna\n" " mekanism är Libera Chat.)" #: ../src/include/commandhelp.h:689 msgid " Identification using mechanism 'plain':" msgstr " Identifiering med mekanism 'plain':" #: ../src/include/commandhelp.h:698 msgid "usage: /say " msgstr "användning: /say " #: ../src/include/commandhelp.h:700 msgid "" "Say a message. This command can be used if you want say something with\n" "a leading command-character, i.e. a slash." msgstr "" "Säg ett meddelande. Detta kommando kan användas om du vill säga något\n" "med ett ledande kommandotecken, dvs ett snedstreck." #: ../src/include/commandhelp.h:705 msgid " /say // A single-line comment in C++" msgstr " /say // En enradskommentar i C++" #: ../src/include/commandhelp.h:710 msgid "usage: /servlist [ []]" msgstr "användning: /servlist [ []]" #: ../src/include/commandhelp.h:712 msgid "" "Lists services currently connected to your IRC network. Arguments,\n" "if given, can be used to restrict the output result." msgstr "" "Listar tjänster som för närvarande är anslutna till ditt\n" "IRC nätverk. Argument, om de ges, kan användas för att begränsa\n" "resultatet." #: ../src/include/commandhelp.h:722 msgid "usage: /servstats [ []]" msgstr "användning: /servstats [ []]" #: ../src/include/commandhelp.h:724 msgid "This command is used to query statistics of a certain IRC server." msgstr "" "Detta kommando används för att fråga efter statistik för en viss IRC\n" "server." #: ../src/include/commandhelp.h:729 msgid "usage: /set [[setting] [value]]" msgstr "användning: /set [[inställning] [värde]]" #: ../src/include/commandhelp.h:731 msgid "Alter Swirc settings." msgstr "Ändra Swirc-inställningar." #: ../src/include/commandhelp.h:736 msgid "" " Bools are case insensitive and can have one of the following\n" " values:" msgstr " Bools är skiftlägesokänsliga och kan ha ett av följande värden:" #: ../src/include/commandhelp.h:738 msgid " - on, true or yes" msgstr " - on, true eller yes" #: ../src/include/commandhelp.h:739 msgid " - off, false or no" msgstr " - off, false eller no" #: ../src/include/commandhelp.h:742 msgid "" " Integers. Swirc implements a min/max value for each integer in\n" " order to keep its value safe. The error log will tell if the\n" " restrictions for an integer aren't within limits and, if so, that\n" " a preprogrammed fallback value is being used instead." msgstr "" " Heltal. Swirc implementerar ett min/max-värde för varje heltal för\n" " att hålla dess värde säkert. Felloggen kommer att berätta om\n" " begränsningarna för ett heltal inte är inom gränserna och i så\n" " fall att ett förprogrammerat reservvärde används istället." #: ../src/include/commandhelp.h:748 msgid " An arbitrary sequence of characters" msgstr " En godtycklig sekvens av tecken" #: ../src/include/commandhelp.h:752 msgid " Output the current values of all settings:" msgstr " Mata ut de aktuella värdena för alla inställningar:" #: ../src/include/commandhelp.h:753 msgid " /set (without any arguments)" msgstr " /set (utan några argument)" #: ../src/include/commandhelp.h:755 msgid " Turn beeps on/off:" msgstr " Slå pip på/av:" #: ../src/include/commandhelp.h:762 msgid "usage: /squery " msgstr "användning: /squery " #: ../src/include/commandhelp.h:764 msgid "" "This command is used similarly to '/msg'. The only difference is that\n" "the recipient MUST be a service." msgstr "" "Detta kommando används på samma sätt som '/msg'. Den enda skillnaden\n" "är att mottagaren MÅSTE vara en tjänst." #: ../src/include/commandhelp.h:770 msgid "usage: /stats [channel]" msgstr "användning: /stats [kanal]" #: ../src/include/commandhelp.h:772 msgid "" "Outputs a channel's user statistics. If the channel argument is left\n" "empty and the active window is an IRC channel, Swirc will output the\n" "user statistics for that channel." msgstr "" "Matar ut en kanals användarstatistik. Om kanalargumentet lämnas tomt\n" "och det aktiva fönstret är en IRC kanal, kommer Swirc att mata ut\n" "användarstatistiken för den kanalen." #: ../src/include/commandhelp.h:779 msgid "usage: /theme [install | list-remote | set ]" msgstr "användning: /theme [install | list-remote | set ]" #: ../src/include/commandhelp.h:781 msgid "Management of themes on-the-fly." msgstr "Hantering av teman." #: ../src/include/commandhelp.h:785 msgid " Install a theme named 'bx':" msgstr " Installera ett tema som heter 'bx':" #: ../src/include/commandhelp.h:788 msgid " List all available themes:" msgstr " Lista alla tillgängliga teman:" #: ../src/include/commandhelp.h:791 msgid " Activate an installed theme with name 'bx':" msgstr " Aktivera ett installerat tema med namnet 'bx':" #: ../src/include/commandhelp.h:797 msgid "usage: /time " msgstr "användning: /time " #: ../src/include/commandhelp.h:799 msgid "" "Send a CTCP TIME request to 'target', which is either a nickname or an\n" "IRC channel." msgstr "" "Skicka en CTCP TIME förfrågan till 'mål', som antingen är ett smeknamn\n" "eller en IRC kanal." #: ../src/include/commandhelp.h:805 msgid "usage: /topic [new topic]" msgstr "användning: /topic [nytt ämne]" #: ../src/include/commandhelp.h:807 msgid "" "Sets a new topic for a channel. If 'new topic' is omitted the action\n" "will be to display the current topic. (The active window must be an\n" "IRC channel.)" msgstr "" "Ställer in ett nytt ämne för en kanal. Om 'nytt ämne' utelämnas kommer\n" "åtgärden att vara att visa det aktuella ämnet. (Det aktiva fönstret\n" "måste vara en IRC kanal.)" #: ../src/include/commandhelp.h:814 msgid "usage: /unban " msgstr "användning: /unban " #: ../src/include/commandhelp.h:816 msgid "Removes a channel ban. (The active window must be an IRC channel.)" msgstr "" "Tar bort ett kanalförbud.\n" "(Det aktiva fönstret måste vara en IRC kanal.)" #: ../src/include/commandhelp.h:826 msgid "usage: /unignore [#]" msgstr "användning: /unignore [#]" #: ../src/include/commandhelp.h:828 msgid "Deletes a regular expression from the ignore list." msgstr "Tar bort ett reguljärt uttryck från ignoreringslistan." #: ../src/include/commandhelp.h:837 msgid "usage: /version " msgstr "användning: /version " #: ../src/include/commandhelp.h:839 msgid "" "Send a CTCP VERSION request to 'target', which is either a nickname or\n" "an IRC channel." msgstr "" "Skicka en CTCP VERSION förfrågan till 'mål', vilket antingen är ett\n" "smeknamn eller en IRC kanal." #: ../src/include/commandhelp.h:845 msgid "usage: /voice " msgstr "användning: /voice " #: ../src/include/commandhelp.h:847 msgid "Gives the channel voice privilege to another user." msgstr "Ger kanal-röst-privilegiet till en annan användare." #: ../src/include/commandhelp.h:852 msgid "usage: /wallops " msgstr "användning: /wallops " #: ../src/include/commandhelp.h:854 msgid "" "The 'wallops'-command is used to send a message to all currently\n" "connected users who have set the 'w' user mode for themselves." msgstr "" "'wallops'-kommandot används för att skicka ett meddelande till alla\n" "för närvarande anslutna användare som har ställt in användarläget 'w'\n" "för sig själva." #: ../src/include/commandhelp.h:860 msgid "usage: /who " msgstr "användning: /who " #: ../src/include/commandhelp.h:862 msgid "" "Generates a query which returns a list of information which matches\n" "the provided 'mask'." msgstr "" "Genererar en fråga som returnerar en lista med information som matchar\n" "den angivna 'masken'." #: ../src/include/commandhelp.h:867 msgid " Show the Libera Chat crew:" msgstr " Visa Libera Chat teamet:" #: ../src/include/commandhelp.h:870 msgid " Show users with a German domain name:" msgstr " Visa användare med ett tyskt domännamn:" #: ../src/include/commandhelp.h:876 msgid "usage: /whois " msgstr "användning: /whois " #: ../src/include/commandhelp.h:878 msgid "Asks after information about another user." msgstr "Frågar efter information om en annan användare." #: ../src/include/commandhelp.h:883 msgid "usage: /wholeft" msgstr "användning: /wholeft" #: ../src/include/commandhelp.h:885 msgid "" "By using this command you can see who left during a network server\n" "split. The command takes no arguments and the active window must be an\n" "IRC channel." msgstr "" "Genom att använda det här kommandot kan du se vem som lämnade under en\n" "nätverksserveruppdelning. Kommandot tar inga argument och det aktiva\n" "fönstret måste vara en IRC kanal." #: ../src/include/commandhelp.h:892 msgid "usage: /znc [*module] " msgstr "användning: /znc [*modul] " #: ../src/include/commandhelp.h:894 msgid "" "Simplifies the communication with ZNC which is a popular\n" "'IRC bouncer'." msgstr "Förenklar kommunikationen med ZNC som är en populär 'IRC studsare'." #: ../src/include/commandhelp.h:899 msgid " Output your ZNC version:" msgstr " Mata ut din ZNC version:" #: ../src/include/commandhelp.h:902 msgid " Identical to the previous example:" msgstr " Identiskt med föregående exempel:" #: ../src/interpreter.cpp:56 ../src/interpreter.cpp:87 #, c-format msgid "%s: fatal: string was truncated" msgstr "%s: fatal: strängen trunkerades" #: ../src/io-loop.c:596 #, c-format msgid " Swirc %s by %s" msgstr " Swirc %s av %s" #: ../src/io-loop.c:597 #, c-format msgid " Compiled on %s%s %s%s" msgstr " Kompilerat %s%s %s%s" #: ../src/io-loop.c:603 #, c-format msgid "Current language %s%s%s" msgstr "Aktuellt språk %s%s%s" #: ../src/io-loop.c:606 #, c-format msgid "Program settings are stored in %s%s%s" msgstr "Programinställningar lagras i %s%s%s" #: ../src/io-loop.c:608 #, c-format msgid "%c%hd%c color pairs have been initialized" msgstr "%c%hd%c färgpar har initierats" #: ../src/io-loop.c:610 msgid "Type /help for a list of commands; or /help " msgstr "Skriv /help för en lista med kommandon; eller /help " #: ../src/io-loop.c:612 msgid "for help of a specific command" msgstr "för hjälp av ett specifikt kommando" #: ../src/io-loop.c:613 msgid "Type F1 for keys" msgstr "Skriv F1 för tangenter" #: ../src/io-loop.c:615 #, c-format msgid "Error log size %s%.1f KB%s" msgstr "Storlek på fellogg %s%.1f KB%s" #: ../src/irc.c:332 #, c-format msgid "%s: no nickname" msgstr "%s: inget smeknamn" #: ../src/irc.c:451 #, c-format msgid "%s: unsupported extension" msgstr "%s: tillägg som inte stöds" #: ../src/irc.c:648 #, c-format msgid "Unknown normal event: %s" msgstr "Okänd normal händelse: %s" #: ../src/irc.c:659 #, c-format msgid "Unknown numeric event: %s" msgstr "Okänd numerisk händelse: %s" #: ../src/irc.c:667 #, c-format msgid "Erroneous event: %s" msgstr "Felaktig händelse: %s" #: ../src/log.c:217 #, c-format msgid "Logging for window (refnum: %d) now off" msgstr "Loggning för fönstret (refnum: %d) är nu av" #: ../src/log.c:221 #, c-format msgid "Logging for window (refnum: %d) now on" msgstr "Loggning för fönstret (refnum: %d) är nu på" #: ../src/main.cpp:84 #, c-format msgid "A duplicate of option -%c found" msgstr "En dubblett av alternativet -%c hittades" #: ../src/main.cpp:144 msgid "" "\n" "Options:\n" "\n" msgstr "" "\n" "Flaggor:\n" "\n" #: ../src/main.cpp:145 msgid " -4 Use IPv4 addresses only\n" msgstr " -4 Använd enbart IPv4 adresser\n" #: ../src/main.cpp:146 msgid " -6 Use IPv6 addresses only\n" msgstr " -6 Använd enbart IPv6 adresser\n" #: ../src/main.cpp:147 msgid " -?, --help Output help\n" msgstr " -?, --help Skriv ut hjälp\n" #: ../src/main.cpp:148 msgid " -C Do not change color definitions\n" msgstr " -C Ändra inte färgdefinitioner\n" #: ../src/main.cpp:149 msgid " -P Permanently disable SASL authentication\n" msgstr " -P Stäng av SASL autentisering permanent\n" #: ../src/main.cpp:150 msgid " -R Disable TLS/SSL peer verification\n" msgstr " -R Stäng av TLS/SSL peer verifikation\n" #: ../src/main.cpp:151 msgid " -S Force TLS\n" msgstr " -S Tvinga TLS\n" #: ../src/main.cpp:156 msgid " -W Equal effect as flag 'p' but non-interactive\n" msgstr "" " -W Samma effekt som flaggan 'p' men icke-interaktiv\n" #: ../src/main.cpp:157 msgid " -X Disable all IRCv3 extensions\n" msgstr " -X Inaktivera alla IRCv3 tillägg\n" #: ../src/main.cpp:158 msgid " -c Connect to IRC server\n" msgstr " -c Anslut till IRC server\n" #: ../src/main.cpp:159 msgid " -d Debug logging\n" msgstr " -d Debug loggning\n" #: ../src/main.cpp:160 msgid " -i Turn on Internet Citizen's Band mode\n" msgstr " -i Sätt på Internet Citizen's Band läge\n" #: ../src/main.cpp:161 msgid " -j A comma-separated list of channels to join\n" msgstr "" " -j En kommaseparerad lista bestående av kanaler att " "öppna\n" #: ../src/main.cpp:162 msgid " -n Online nickname\n" msgstr " -n Online nickname\n" #: ../src/main.cpp:163 msgid "" " -p Query for server password (for private servers)\n" msgstr "" " -p Fråga efter serverlösenord (för privata servrar)\n" #: ../src/main.cpp:164 msgid " -r Your real name\n" msgstr " -r Ditt riktiga namn\n" #: ../src/main.cpp:165 msgid " -u Your username\n" msgstr " -u Ditt användarnamn\n" #: ../src/main.cpp:166 msgid " -v, --version Output Swirc version\n" msgstr " -v, --version Visa Swirc version\n" #: ../src/main.cpp:167 msgid " -x Config file\n" msgstr " -x Konfigurationsfil\n" #: ../src/main.cpp:230 msgid "Unhandled exception!" msgstr "Ohanterat undantag!" #: ../src/main.cpp:290 #, c-format msgid "Usage: %s [OPTION] ...\n" msgstr "Användning: %s [FLAGGA] ...\n" #: ../src/main.cpp:380 msgid "forbidden channel name characters" msgstr "förbjudna kanalnamnstecken" #: ../src/main.cpp:532 #, c-format msgid "%s: -%c: unrecognized option" msgstr "%s: -%c: okänt alternativ" #: ../src/main.cpp:537 #, c-format msgid "%s: -%c: option argument missing" msgstr "%s: -%c: alternativargument saknas" #: ../src/main.cpp:706 msgid "fatal: failed to initialize signal handling" msgstr "fatal: misslyckades med att initiera signalhantering" #: ../src/main.cpp:728 msgid "fatal: running the program with root privileges is forbidden" msgstr "fatal: att köra programmet med root privilegier är förbjudet" #: ../src/main.cpp:750 msgid "Initialization of the Curses library not possible" msgstr "Initiering av Curses biblioteket är inte möjlig" #: ../src/messagetags.c:150 #, c-format msgid "%s: server time error" msgstr "%s: servertidsfel" #: ../src/nestHome.c:143 #, c-format msgid "%s exists. However; it isn't a directory." msgstr "%s finns. Dock; det är inte en katalog." #: ../src/nestHome.c:147 ../src/nestHome.c:150 msgid "Cannot make directory" msgstr "Kan inte skapa katalog" #: ../src/nestHome.c:177 msgid "Please decrypt your SASL password..." msgstr "Vänligen dekryptera ditt SASL lösenord..." #: ../src/nestHome.c:178 msgid "(One attempt)" msgstr "(Ett försök)" #: ../src/nestHome.c:186 msgid "Password: " msgstr "Lösenord: " #: ../src/nestHome.c:216 msgid "Decryption failed" msgstr "Dekrypteringen misslyckades" #: ../src/nestHome.c:219 msgid "Wrong password" msgstr "Fel lösenord" #: ../src/nestHome.c:230 msgid "The password seems reasonable" msgstr "Lösenordet verkar rimligt" #: ../src/nestHome.c:242 msgid "Press " msgstr "Tryck på " #: ../src/nestHome.c:267 ../src/nestHome.c:278 ../src/nestHome.c:337 #, c-format msgid "%s exists -- but isn't a regular file." msgstr "%s finns -- men är inte en vanlig fil." #: ../src/nestHome.c:270 ../src/nestHome.c:345 #, c-format msgid "%s no such file or directory. Exiting..." msgstr "%s ingen sådan fil eller katalog. Avslutar..." #: ../src/nestHome.c:301 msgid "Warning!" msgstr "Varning!" #: ../src/nestHome.c:302 msgid "" "A decrypted SASL password was found in the read configuration file -- " "cannot continue!" msgstr "" "Ett dekrypterat SASL lösenord hittades i den lästa konfigurationsfilen -- " "kan inte fortsätta!" #: ../src/nestHome.c:333 msgid "The item 'theme' in the user configuration file holds no data. Error." msgstr "" "Objektet 'theme' i användarkonfigurationsfilen innehåller inga data. Fel." #: ../src/nestHome.c:358 msgid "Can't resolve the home path!" msgstr "Kan inte lösa hemvägen!" #: ../src/network.cpp:183 msgid "Hostname checking failed!" msgstr "Värdnamnskontrollen misslyckades!" #: ../src/network.cpp:185 msgid "Hostname checking OK!" msgstr "Värdnamnskontrollen OK!" #: ../src/network.cpp:242 msgid "Connected!" msgstr "Ansluten!" #: ../src/network.cpp:311 msgid "Get a list of IP addresses completed" msgstr "Få en lista över IP adresser färdig" #: ../src/network.cpp:539 #, c-format msgid "Connecting to %s (%s)" msgstr "Ansluter till %s (%s)" #: ../src/network.cpp:807 msgid "Connection to IRC server lost" msgstr "Anslutningen till IRC servern förlorad" #: ../src/network.cpp:818 msgid "Disconnected" msgstr "Frånkopplad" #: ../src/readline.c:623 msgid "--------------- Keys ---------------" msgstr "--------------- Tangenter ---------------" #: ../src/readline.c:625 msgid "CTRL+a Move to beginning of line" msgstr "CTRL+a Flytta till början av raden" #: ../src/readline.c:626 msgid "CTRL+e Move to end of line" msgstr "CTRL+e Flytta till slutet av raden" #: ../src/readline.c:627 msgid "CTRL+b Move cursor backward" msgstr "CTRL+b Flytta markören bakåt" #: ../src/readline.c:628 msgid "CTRL+f Move cursor forward" msgstr "CTRL+f Flytta markören framåt" #: ../src/readline.c:629 msgid "CTRL+d Delete" msgstr "CTRL+d Ta bort" #: ../src/readline.c:630 msgid "CTRL+g Clear readline input" msgstr "CTRL+g Töm inmatning" #: ../src/readline.c:631 msgid "CTRL+l Toggle logging on/off" msgstr "CTRL+l Växla loggning på/av" #: ../src/readline.c:632 msgid "CTRL+n Next window" msgstr "CTRL+n Nästa fönster" #: ../src/readline.c:633 msgid "CTRL+p Previous window" msgstr "CTRL+p Föregående fönster" #: ../src/readline.c:634 msgid "CTRL+w List all windows" msgstr "CTRL+w Lista alla fönster" #: ../src/readline.c:635 msgid "PG UP Scroll up" msgstr "PG UP Skrolla upp" #: ../src/readline.c:636 msgid "PG DOWN Scroll down" msgstr "PG DOWN Skrolla ned" #: ../src/readline.c:637 msgid "Up arrow History previous" msgstr "Up arrow Föregående kommando" #: ../src/readline.c:638 msgid "Down arrow History next" msgstr "Down arrow Nästa kommando" #: ../src/readline.c:639 msgid "F2 Spell word" msgstr "F2 Stava ord" #: ../src/readline.c:640 msgid "F3 Scroll nicklist up" msgstr "F3 Skrolla nicklistan upp" #: ../src/readline.c:641 msgid "F4 Scroll nicklist down" msgstr "F4 Skrolla nicklistan ned" #: ../src/readline.c:642 msgid "F11 Close window" msgstr "F11 Stäng fönster" #: ../src/readline.c:643 msgid "F12 Close all private conversations" msgstr "F12 Stäng alla privata konversationer" #: ../src/sig-w32.c:26 msgid "Abnormal termination" msgstr "Avbruten" #: ../src/sig-w32.c:27 msgid "Floating-point error" msgstr "Flyttalsfel" #: ../src/sig-w32.c:28 msgid "Illegal instruction" msgstr "Otillåten instruktion" #: ../src/sig-w32.c:29 msgid "Illegal storage access" msgstr "Segmenteringsfel" #: ../src/sig-w32.c:30 msgid "Termination request" msgstr "Avslutad" #: ../src/spell.cpp:137 ../src/spell.cpp:141 #, c-format msgid "%s: %s not found" msgstr "%s: %s hittades inte" #: ../src/spell.cpp:317 msgid "no more suggestions" msgstr "inga fler förslag" #: ../src/spell.cpp:344 msgid "suggestions:" msgstr "förslag:" #: ../src/spell.cpp:372 #, c-format msgid "%ls is correct" msgstr "%ls är korrekt" #: ../src/spell.cpp:377 #, c-format msgid "%ls is incorrect" msgstr "%ls är felaktigt" #: ../src/statusbar.cpp:222 msgid "Log: " msgstr "Logg: " #: ../src/statusbar.cpp:227 msgid "-- MORE --" msgstr "-- MER --" #: ../src/tls-server.cpp:205 msgid "Already accepting DCC connections..." msgstr "Accepterar redan DCC anslutningar..." #: ../src/tls-server.cpp:229 #, c-format msgid "Accepting DCC connections at port: %d" msgstr "Accepterar DCC anslutningar på port: %d" #: ../src/tls-server.cpp:241 msgid "Out of memory" msgstr "Slut på minne" #: ../src/tls-server.cpp:250 msgid "Stopped accepting DCC connections" msgstr "Slutade acceptera DCC anslutningar" #, c-format #~ msgid "%s: cannot find time" #~ msgstr "%s: kan inte hitta tid" #, c-format #~ msgid "%s: empty message" #~ msgstr "%s: tomt meddelande" #, c-format #~ msgid "%s: too many semicolons" #~ msgstr "%s: för många semikolon" swirc-3.5.5/po/targets/000077500000000000000000000000001501213070300147325ustar00rootroot00000000000000swirc-3.5.5/po/targets/lang/000077500000000000000000000000001501213070300156535ustar00rootroot00000000000000swirc-3.5.5/po/targets/lang/de.mk000066400000000000000000000003311501213070300165710ustar00rootroot00000000000000################ ## German ## ################ de/$(PKG).po: mkdir -p de msginit --input=$(PKG).pot --output-file=de/$(PKG).po --locale=de_DE.UTF-8 de/$(PKG).mo: msgfmt --output-file=de/$(PKG).mo de/$(PKG).po swirc-3.5.5/po/targets/lang/fi.mk000066400000000000000000000003341501213070300166020ustar00rootroot00000000000000################# ## Finnish ## ################# fi/$(PKG).po: mkdir -p fi msginit --input=$(PKG).pot --output-file=fi/$(PKG).po --locale=fi_FI.UTF-8 fi/$(PKG).mo: msgfmt --output-file=fi/$(PKG).mo fi/$(PKG).po swirc-3.5.5/po/targets/lang/fr.mk000066400000000000000000000003311501213070300166100ustar00rootroot00000000000000################ ## French ## ################ fr/$(PKG).po: mkdir -p fr msginit --input=$(PKG).pot --output-file=fr/$(PKG).po --locale=fr_FR.UTF-8 fr/$(PKG).mo: msgfmt --output-file=fr/$(PKG).mo fr/$(PKG).po swirc-3.5.5/po/targets/lang/sv.mk000066400000000000000000000003341501213070300166340ustar00rootroot00000000000000################# ## Swedish ## ################# sv/$(PKG).po: mkdir -p sv msginit --input=$(PKG).pot --output-file=sv/$(PKG).po --locale=sv_SE.UTF-8 sv/$(PKG).mo: msgfmt --output-file=sv/$(PKG).mo sv/$(PKG).po swirc-3.5.5/po/targets/merge.mk000066400000000000000000000003071501213070300163620ustar00rootroot00000000000000merge: $(MSGMERGE) $(MMFLAGS) de/$(PKG).po $(PKG).pot $(MSGMERGE) $(MMFLAGS) fi/$(PKG).po $(PKG).pot $(MSGMERGE) $(MMFLAGS) fr/$(PKG).po $(PKG).pot $(MSGMERGE) $(MMFLAGS) sv/$(PKG).po $(PKG).pot swirc-3.5.5/posixshell/000077500000000000000000000000001501213070300150355ustar00rootroot00000000000000swirc-3.5.5/posixshell/check_etext.sh000066400000000000000000000022401501213070300176550ustar00rootroot00000000000000# check_etext.sh: # Check whether the etext segment is usable # # SPDX-FileCopyrightText: Copyright 2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause check_etext () { local _tmpfile _srcfile _out local _val=1 printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #include int global; static int valid(const void *ptr) { extern char etext; return (ptr != NULL && ((const char *) ptr) > &etext); } int main(void) { int local; if (valid(&local) && valid(&global)) return 99; return 1; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking whether the etext segment is usable..." ${CC} ${CFLAGS} -Werror "$_srcfile" -o "$_out" ${LDFLAGS} \ >/dev/null 2>&1 if [ $? -eq 0 ]; then eval "$_out" _val=$? fi if [ ${_val} -eq 99 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_ETEXT_SEGMENT=1 EOF else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/check_strcasestr.sh000066400000000000000000000021671501213070300207310ustar00rootroot00000000000000# check_strcasestr.sh: # Check whether strcasestr() exists # # SPDX-FileCopyrightText: Copyright 2022-2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause check_strcasestr () { local _tmpfile _srcfile _out printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #if (defined(__gnu_hurd__) || defined(__linux__)) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include int main(void) { char *str; str = strcasestr("swirc IRC client", "irc"); return (str ? 0 : 1); } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking for strcasestr()..." ${CC} ${CFLAGS} -Werror "$_srcfile" -o "$_out" ${LDFLAGS} \ >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_STRCASESTR=1 EOF else echo "no" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_STRCASESTR=0 EOF fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/fix_cflags.sh000066400000000000000000000003661501213070300175030ustar00rootroot00000000000000# fix_cflags.sh fix_cflags() { if [ -x "$(which pkg-config)" ]; then local _hdr_search_path="$(pkg-config --cflags-only-I ncursesw)" if [ -n "$_hdr_search_path" ]; then echo "CPPFLAGS += ${_hdr_search_path}" >>$MAKE_DEF_FILE fi fi } swirc-3.5.5/posixshell/link_with_gnu_libidn.sh000066400000000000000000000020771501213070300215610ustar00rootroot00000000000000# link_with_gnu_libidn.sh: # Link with GNU libidn # # SPDX-FileCopyrightText: Copyright 2021-2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause link_with_gnu_libidn () { local _tmpfile _srcfile _out _libs printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" _libs="-lidn" cat <"$_srcfile" #include #include #include int main(void) { const char *cp; idn_free(NULL); if ((cp = stringprep_check_version(NULL)) != NULL) puts(cp); return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking for gnu libidn..." ${CC} ${CFLAGS} -Werror "$_srcfile" -o "$_out" ${LDFLAGS} ${_libs} \ >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_LIBIDN=1 LDLIBS += ${_libs} EOF else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/link_with_gnu_libintl.sh000066400000000000000000000037331501213070300217550ustar00rootroot00000000000000# link_with_gnu_libintl.sh: # Link with GNU LibIntl # # SPDX-FileCopyrightText: Copyright 2022-2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause check_intl_header () { local _tmpfile _srcfile _out printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #include int main(void) { return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking for 'libintl.h'..." ${CC} ${CFLAGS} "$_srcfile" -o "$_out" >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_LIBINTL_H=1 EOF case "$(uname -s)" in "Darwin" | "FreeBSD" | "NetBSD" | "OpenBSD") cat <>$MAKE_DEF_FILE LDLIBS += -lintl EOF ;; *) ;; esac else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } check_intl_setlocale () { local _tmpfile _srcfile _out _libs printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #include #ifdef setlocale #undef setlocale #endif int main(void) { libintl_setlocale(LC_ALL, ""); return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking for libintl_setlocale()..." if [ "$(uname -s)" = "Linux" ]; then _libs="" else _libs="-lintl" fi ${CC} ${CFLAGS} "$_srcfile" -o "$_out" ${LDFLAGS} ${_libs} \ >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_LIBINTL_SETLOCALE=1 EOF else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } link_with_gnu_libintl () { check_intl_header check_intl_setlocale } swirc-3.5.5/posixshell/link_with_hunspell.sh000066400000000000000000000024341501213070300212760ustar00rootroot00000000000000# link_with_hunspell.sh: # Link with Hunspell # # SPDX-FileCopyrightText: Copyright 2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause link_with_hunspell () { local _tmpfile _srcfile _out local _includes _libpath _libs printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.cpp" _out="${_tmpfile}.out" cat <"$_srcfile" #include #include int main(void) { Hunhandle *hh; if ((hh = Hunspell_create("", "")) != NULL) Hunspell_destroy(hh); return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi _includes="$(pkg-config --cflags-only-I hunspell)" _libpath="$(pkg-config --libs-only-L hunspell)" _libs="$(pkg-config --libs-only-l hunspell)" printf "checking for hunspell..." ${CXX} ${CXXFLAGS} ${_includes%%/hunspell} -Werror "$_srcfile" -o \ "$_out" ${LDFLAGS} ${_libpath} ${_libs} >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_HUNSPELL=1 CPPFLAGS += ${_includes%%/hunspell} LDFLAGS += ${_libpath} LDLIBS += ${_libs} EOF else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/link_with_libiconv.sh000066400000000000000000000024651501213070300212550ustar00rootroot00000000000000# link_with_libiconv.sh: # Link with GNU LibIconv # # SPDX-FileCopyrightText: Copyright 2022-2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause link_with_libiconv () { local _tmpfile _srcfile _out _libs printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #include #include int main(void) { iconv_t cd; if ((cd = iconv_open("ISO-8859-1", "UTF-8")) == ((iconv_t) -1)) return 1; else if (iconv_close(cd) != 0) return 1; puts("iconv works!"); return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi printf "checking for gnu libiconv..." if [ "$(uname -s)" = "GNU" ] || [ "$(uname -s)" = "Linux" ]; then _libs="" else _libs="-liconv" fi ${CC} ${CFLAGS} "$_srcfile" -o "$_out" ${LDFLAGS} ${_libs} \ >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DHAVE_LIBICONV=1 EOF case "$(uname -s)" in "Darwin" | "FreeBSD" | "NetBSD" | "OpenBSD") cat <>$MAKE_DEF_FILE LDLIBS += -liconv EOF ;; *) ;; esac else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/link_with_libnotify.sh000066400000000000000000000023061501213070300214410ustar00rootroot00000000000000# link_with_libnotify.sh: # Link with LibNotify # # SPDX-FileCopyrightText: Copyright 2021-2023 Markus Uhlin # SPDX-License-Identifier: BSD-3-Clause link_with_libnotify () { local _tmpfile _srcfile _out local _includes _libs printf "creating temp file..." _tmpfile=$(mktemp) || { echo "error"; exit 1; } echo "ok" _srcfile="${_tmpfile}.c" _out="${_tmpfile}.out" cat <"$_srcfile" #include int main(void) { NotifyNotification *nn; if (!notify_init("Swirc IRC client")) return 1; (void) nn; notify_uninit(); return 0; } EOF if [ ! -f "$_srcfile" ]; then echo "failed to create $_srcfile" exit 1 fi _includes="$(pkg-config --cflags-only-I libnotify)" _libs="$(pkg-config --libs-only-l libnotify)" printf "checking whether to define 'USE_LIBNOTIFY=1'..." ${CC} ${CFLAGS} ${_includes} -Werror "$_srcfile" -o "$_out" ${LDFLAGS} \ ${_libs} >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "yes" cat <>$MAKE_DEF_FILE CPPFLAGS += -DUSE_LIBNOTIFY=1 CPPFLAGS += ${_includes} LDLIBS += ${_libs} EOF else echo "no" fi echo "cleaning..." test -f "$_tmpfile" && rm -f "$_tmpfile" test -f "$_srcfile" && rm -f "$_srcfile" test -f "$_out" && rm -f "$_out" } swirc-3.5.5/posixshell/os_BSD.sh000066400000000000000000000011471501213070300165050ustar00rootroot00000000000000# -------------------------------------------------- # Generic BSD (Berkeley Software Distribution) # -------------------------------------------------- os_BSD () { cat <>"${MAKE_DEF_FILE}" CC = cc CFLAGS = -O2 -Wall -pipe -std=c17 CXX = c++ CXXFLAGS = -O2 -Wall -pipe -std=c++17 # C preprocessor flags CPPFLAGS = -DBSD=1\\ -DNDEBUG=1\\ -DUNIX=1\\ -D_XOPEN_SOURCE_EXTENDED=1\\ -I/usr/local/include LDFLAGS = -L/usr/local/lib LDLIBS = -lcrypto\\ -lcurl\\ -lncursesw\\ -lpanelw\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=cc fi if [ -z ${CXX+x} ]; then CXX=c++ fi set_common } swirc-3.5.5/posixshell/os_GNU.sh000066400000000000000000000013441501213070300165250ustar00rootroot00000000000000# -------------------------------------------------- # GNU Hurd # -------------------------------------------------- os_GNU () { cat <>"${MAKE_DEF_FILE}" CC = gcc CFLAGS = -O2\\ -Wall\\ -Wsign-compare\\ -Wstrict-prototypes\\ -fstack-protector-strong\\ -g\\ -pipe\\ -std=c11 CXX = g++ CXXFLAGS = -O2 -Wall -fstack-protector-strong -g -pipe -std=c++17 # C preprocessor flags CPPFLAGS = -DHURD=1\\ -DNDEBUG=1\\ -DUNIX=1\\ -D_FORTIFY_SOURCE=2\\ -D_GNU_SOURCE=1\\ -I/usr/local/include LDFLAGS = -L/usr/local/lib\\ -Wl,-z,now\\ -Wl,-z,relro LDLIBS = -lcrypto\\ -lcurl\\ -lncursesw\\ -lpanelw\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=gcc fi if [ -z ${CXX+x} ]; then CXX=g++ fi set_common } swirc-3.5.5/posixshell/os_LINUX.sh000066400000000000000000000015671501213070300170020ustar00rootroot00000000000000# -------------------------------------------------- # GNU/Linux # -------------------------------------------------- os_LINUX () { cat <>"${MAKE_DEF_FILE}" CC = gcc CFLAGS = -O2\\ -Wall\\ -Wformat-security\\ -Wshadow\\ -Wsign-compare\\ -Wstrict-prototypes\\ -fstack-protector-strong\\ -g\\ -pipe\\ -std=c11 CXX = g++ CXXFLAGS = -O2\\ -Wall\\ -Wformat-security\\ -Wshadow\\ -fstack-protector-strong\\ -g\\ -pipe\\ -std=c++17 # C preprocessor flags CPPFLAGS = -DLINUX=1\\ -DNDEBUG=1\\ -DUNIX=1\\ -D_FORTIFY_SOURCE=3\\ -D_POSIX_C_SOURCE=200809L\\ -D_XOPEN_SOURCE=500\\ -D_XOPEN_SOURCE_EXTENDED=1\\ -I/usr/local/include LDFLAGS = -L/usr/local/lib\\ -Wl,-z,now\\ -Wl,-z,relro LDLIBS = -lcrypto\\ -lcurl\\ -lncursesw\\ -lpanelw\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=gcc fi if [ -z ${CXX+x} ]; then CXX=g++ fi set_common } swirc-3.5.5/posixshell/os_LINUX_suncc.sh000066400000000000000000000017031501213070300201650ustar00rootroot00000000000000# -------------------------------------------------- # GNU/Linux (with Sun C compiler) # -------------------------------------------------- os_LINUX_suncc () { cat <>"${MAKE_DEF_FILE}" CC = suncc CFLAGS = -O2\\ -Wp,-I/usr/include/x86_64-linux-gnu\\ -errtags\\ -pedantic\\ -std=c11\\ -xannotate\\ -xatomic=studio\\ -xprevise\\ -xsecure_code_analysis CXX = sunCC CXXFLAGS = -O2\\ -Wp,-I/usr/include/x86_64-linux-gnu\\ -errtags\\ -pedantic\\ -std=c++14\\ -xannotate\\ -xatomic=studio\\ -xprevise\\ -xsecure_code_analysis # C preprocessor flags CPPFLAGS = -DLINUX=1\\ -DUNIX=1\\ -D_FORTIFY_SOURCE=2\\ -D_POSIX_C_SOURCE=200809L\\ -D_XOPEN_SOURCE=500\\ -D_XOPEN_SOURCE_EXTENDED=1\\ -I/usr/local/include LDFLAGS = -L/usr/local/lib\\ -xannotate\\ -xprevise LDLIBS = -lcrypto\\ -lcurl\\ -lncursesw\\ -lpanelw\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=suncc fi if [ -z ${CXX+x} ]; then CXX=sunCC fi set_common } swirc-3.5.5/posixshell/os_MAC.sh000066400000000000000000000013741501213070300164770ustar00rootroot00000000000000# -------------------------------------------------- # Mac OS X # -------------------------------------------------- os_MAC () { cat <>"${MAKE_DEF_FILE}" CC = clang CFLAGS = -O2 -Wall -pipe -std=c11 CXX = clang++ CXXFLAGS = -O2 -Wall -pipe -std=c++17 # C preprocessor flags CPPFLAGS = -DNCURSES_OPAQUE=1\\ -DNDEBUG=1\\ -DOS_X=1\\ -DUNIX=1\\ -D_XOPEN_SOURCE_EXTENDED=1\\ -I/usr/local/include\\ -I/usr/local/opt/libressl/include\\ -I/opt/homebrew/opt/libressl/include LDFLAGS = -L/usr/local/lib\\ -L/usr/local/opt/libressl/lib\\ -L/opt/homebrew/opt/libressl/lib LDLIBS = -lcrypto\\ -lcurl\\ -lncurses\\ -lpanel\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=clang fi if [ -z ${CXX+x} ]; then CXX=clang++ fi set_common } swirc-3.5.5/posixshell/os_NETBSD.sh000066400000000000000000000012241501213070300170500ustar00rootroot00000000000000# -------------------------------------------------- # NetBSD # -------------------------------------------------- os_NETBSD () { cat <>"${MAKE_DEF_FILE}" CC = cc CFLAGS = -O2 -Wall -std=c11 CXX = c++ CXXFLAGS = -O2 -Wall -std=c++17 # C preprocessor flags CPPFLAGS = -DBSD=1\\ -DUNIX=1\\ -D_XOPEN_SOURCE_EXTENDED=1\\ -I/usr/local/include\\ -I/usr/pkg/include/ncursesw\\ -I/usr/pkg/include LDFLAGS = -L/usr/local/lib\\ -L/usr/pkg/lib\\ -Wl,-rpath,/usr/pkg/lib LDLIBS = -lcrypto\\ -lcurl\\ -lgnupanelw\\ -lncursesw\\ -lpthread\\ -lssl EOF if [ -z ${CC+x} ]; then CC=cc fi if [ -z ${CXX+x} ]; then CXX=c++ fi set_common } swirc-3.5.5/posixshell/set_common.sh000066400000000000000000000003271501213070300175360ustar00rootroot00000000000000set_common () { if [ -z ${CFLAGS+x} ]; then CFLAGS="-I/usr/local/include" fi if [ -z ${CXXFLAGS+x} ]; then CXXFLAGS="-I/usr/local/include" fi if [ -z ${LDFLAGS+x} ]; then LDFLAGS="-L/usr/local/lib" fi } swirc-3.5.5/src/000077500000000000000000000000001501213070300134325ustar00rootroot00000000000000swirc-3.5.5/src/DesktopNotificationManagerCompat.cpp000066400000000000000000000223161501213070300225610ustar00rootroot00000000000000/* * Copyright (c) Microsoft. All rights reserved. * This code is licensed under the MIT License (MIT). * * THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE. */ #include "DesktopNotificationManagerCompat.hpp" #include #include #define RETURN_IF_FAILED(hr)\ do {\ HRESULT _hrTemp = hr;\ \ if (FAILED(_hrTemp)) {\ return _hrTemp;\ }\ } while (false) using namespace ABI::Windows::Data::Xml::Dom; using namespace Microsoft::WRL::Wrappers; using namespace Microsoft::WRL; namespace DesktopNotificationManagerCompat { HRESULT EnsureRegistered(); HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[]); bool IsRunningAsUwp(); bool s_hasCheckedIsRunningAsUwp = false; bool s_isRunningAsUwp = false; bool s_registeredActivator = false; bool s_registeredAumidAndComServer = false; std::wstring s_aumid; HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid) { if (IsRunningAsUwp()) { /* * Clear the AUMID since Desktop Bridge * doesn't use it, and then we're * done. Desktop Bridge apps are registered * with platform through their manifest. Their * LocalServer32 key is also registered * through their manifest. */ s_aumid.clear(); s_registeredAumidAndComServer = true; return S_OK; } /* * Copy the aumid */ s_aumid = std::wstring(aumid); /* * Get the EXE path */ wchar_t exePath[MAX_PATH] = { 0L }; DWORD charWritten = ::GetModuleFileName(nullptr, exePath, ARRAYSIZE(exePath)); RETURN_IF_FAILED(charWritten > 0 ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); /* * Register the COM server */ RETURN_IF_FAILED(RegisterComServer(clsid, exePath)); s_registeredAumidAndComServer = true; return S_OK; } HRESULT RegisterActivator() { /* * Module needs a callback registered * before it can be used. Since we don't care about * when it shuts down, we'll pass an empty lambda * here. */ Module::Create([] {}); /* * If a local server process only hosts the COM object * then COM expects the COM server host to shutdown * when the references drop to zero. Since the user * might still be using the program after activating * the notification, we don't want to shutdown * immediately. Incrementing the object count tells * COM that we aren't done yet. */ Module::GetModule().IncrementObjectCount(); RETURN_IF_FAILED(Module:: GetModule().RegisterObjects()); s_registeredActivator = true; return S_OK; } HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[]) { /* Turn the GUID into a string */ OLECHAR *clsidOlechar; StringFromCLSID(clsid, &clsidOlechar); std::wstring clsidStr(clsidOlechar); ::CoTaskMemFree(clsidOlechar); /** * Create the subkey * * Something like: * SOFTWARE\Classes\CLSID\ * {23A5B06E-20BB-4E7E-A0AC-6982ED6A6041}\LocalServer32 */ std::wstring subKey = LR"(SOFTWARE\Classes\CLSID\)" + clsidStr + LR"(\LocalServer32)"; std::wstring exePathStr(exePath); exePathStr = L"\"" + exePathStr + L"\" " + TOAST_ACTIVATED_LAUNCH_ARG; exePathStr.append(L"--"); /** * We don't need to worry about overflow here as * ::GetModuleFileName won't return anything bigger * than the max file system path (much fewer than max * of DWORD). */ DWORD dataSize = static_cast ((exePathStr.length() + 1) * sizeof(WCHAR)); /* * Register the EXE for the COM server */ return HRESULT_FROM_WIN32(::RegSetKeyValue(HKEY_CURRENT_USER, subKey.c_str(), nullptr, REG_SZ, reinterpret_cast(exePathStr.c_str()), dataSize)); } HRESULT CreateToastNotifier(IToastNotifier **notifier) { ComPtr toastStatics; RETURN_IF_FAILED(EnsureRegistered()); RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory (HStringReference (RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics)); if (s_aumid.empty()) { return toastStatics->CreateToastNotifier(notifier); } else { return toastStatics->CreateToastNotifierWithId (HStringReference(s_aumid.c_str()).Get(), notifier); } } HRESULT CreateXmlDocumentFromString(const wchar_t *xmlString, IXmlDocument **doc) { ComPtr answer; ComPtr docIO; RETURN_IF_FAILED(Windows::Foundation::ActivateInstance (HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(), &answer)); RETURN_IF_FAILED(answer.As(&docIO)); RETURN_IF_FAILED(docIO->LoadXml(HStringReference(xmlString).Get())); return answer.CopyTo(doc); } HRESULT CreateToastNotification(IXmlDocument *content, IToastNotification **notification) { ComPtr factory; RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory (HStringReference (RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory)); return factory->CreateToastNotification(content, notification); } HRESULT get_History(std::unique_ptr *history) { ComPtr toastStatics; ComPtr toastStatics2; ComPtr nativeHistory; RETURN_IF_FAILED(EnsureRegistered()); RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory (HStringReference (RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics)); RETURN_IF_FAILED(toastStatics.As(&toastStatics2)); RETURN_IF_FAILED(toastStatics2->get_History(&nativeHistory)); *history = std::make_unique (DesktopNotificationHistoryCompat(s_aumid.c_str(), nativeHistory)); return S_OK; } bool CanUseHttpImages() { return IsRunningAsUwp(); } HRESULT EnsureRegistered() { /* * If not registered AUMID yet */ if (!s_registeredAumidAndComServer) { if (IsRunningAsUwp()) { /* * Implicitly registered, all good! */ s_registeredAumidAndComServer = true; } else { /* * Otherwise, incorrect usage, must * call RegisterAumidAndComServer * first. */ return E_ILLEGAL_METHOD_CALL; } } /* * If not registered activator yet */ if (!s_registeredActivator) { /* * Incorrect usage, must call * RegisterActivator first. */ return E_ILLEGAL_METHOD_CALL; } return S_OK; } bool IsRunningAsUwp() { if (!s_hasCheckedIsRunningAsUwp) { /* * https://stackoverflow.com/questions/39609643/ * determine-if-c-application-is-running-as-a-uwp-app- * in-desktop-bridge-project */ UINT32 length; wchar_t packageFamilyName [PACKAGE_FAMILY_NAME_MAX_LENGTH + 1] = { 0L }; s_isRunningAsUwp = (GetPackageFamilyName(GetCurrentProcess(), &length, packageFamilyName) == ERROR_SUCCESS); s_hasCheckedIsRunningAsUwp = true; } return s_isRunningAsUwp; } } DesktopNotificationHistoryCompat::DesktopNotificationHistoryCompat (const wchar_t *aumid, ComPtr history) : m_history(history) { m_aumid = std::wstring(aumid); // m_history = history; } HRESULT DesktopNotificationHistoryCompat::Clear() { if (m_aumid.empty()) { return m_history->Clear(); } else { return m_history->ClearWithId (HStringReference(m_aumid.c_str()).Get()); } } HRESULT DesktopNotificationHistoryCompat::GetHistory(ABI::Windows::Foundation:: Collections::IVectorView **toasts) { ComPtr history2; RETURN_IF_FAILED(m_history.As(&history2)); if (m_aumid.empty()) { return history2->GetHistory(toasts); } else { return history2->GetHistoryWithId (HStringReference(m_aumid.c_str()).Get(), toasts); } } HRESULT DesktopNotificationHistoryCompat::Remove(const wchar_t *tag) { if (m_aumid.empty()) { return m_history->Remove(HStringReference(tag).Get()); } else { return m_history->RemoveGroupedTagWithId (HStringReference(tag).Get(), HStringReference(L"").Get(), HStringReference(m_aumid.c_str()).Get()); } } HRESULT DesktopNotificationHistoryCompat::RemoveGroupedTag(const wchar_t *tag, const wchar_t *group) { if (m_aumid.empty()) { return m_history->RemoveGroupedTag(HStringReference(tag).Get(), HStringReference(group).Get()); } else { return m_history->RemoveGroupedTagWithId (HStringReference(tag).Get(), HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get()); } } HRESULT DesktopNotificationHistoryCompat::RemoveGroup(const wchar_t *group) { if (m_aumid.empty()) { return m_history->RemoveGroup(HStringReference(group).Get()); } else { return m_history->RemoveGroupWithId (HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get()); } } swirc-3.5.5/src/DesktopNotificationManagerCompat.hpp000066400000000000000000000103451501213070300225650ustar00rootroot00000000000000/* * Copyright (c) Microsoft. All rights reserved. * This code is licensed under the MIT License (MIT). * * THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE. */ #pragma once #include #include #include #include #include #define TOAST_ACTIVATED_LAUNCH_ARG L"-T" using namespace ABI::Windows::UI::Notifications; class DesktopNotificationHistoryCompat; namespace DesktopNotificationManagerCompat { /** * If not running under the Desktop Bridge, you must call this * method to register your AUMID with the Compat library and * to register your COM CLSID and EXE in LocalServer32 * registry. Feel free to call this regardless, and we will * no-op if running under Desktop Bridge. Call this upon * application startup, before calling any other APIs. * * @param aumid An AUMID that uniquely identifies your * application. * @param clsid The CLSID of your NotificationActivator class. */ HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid); /** * Registers your module to handle COM activations. Call this * upon application startup. */ HRESULT RegisterActivator(); /** * Creates a toast notifier. You must have called * RegisterActivator first (and also RegisterAumidAndComServer * if you're a classic Win32 app), or this will throw an * exception. */ HRESULT CreateToastNotifier(IToastNotifier **); /** * Creates an XmlDocument initialized with the specified * string. This is simply a convenience helper method. */ HRESULT CreateXmlDocumentFromString(const wchar_t *, ABI::Windows::Data::Xml::Dom::IXmlDocument **); /** * Creates a toast notification. This is simply a convenience * helper method. */ HRESULT CreateToastNotification(ABI::Windows::Data::Xml::Dom:: IXmlDocument *, IToastNotification **); /** * Gets the DesktopNotificationHistoryCompat object. * * You must have called RegisterActivator first (and also * RegisterAumidAndComServer if you're a classic Win32 app), * or this will throw an exception. */ HRESULT get_History(std::unique_ptr *history); /** * Gets a boolean representing whether http images can be used * within toasts. This is true if running under Desktop * Bridge. */ bool CanUseHttpImages(); } class DesktopNotificationHistoryCompat { public: /** * Removes all notifications sent by this app from action * center. */ HRESULT Clear(); /** * Gets all notifications sent by this app that are currently * still in Action Center. */ HRESULT GetHistory(ABI::Windows::Foundation::Collections::IVectorView **); /** * Removes an individual toast, with the specified tag label, * from action center. * * @param tag The tag label of the toast notification to be * removed. */ HRESULT Remove(const wchar_t *tag); /** * Removes a toast notification from the action using the * notification's tag and group labels. * * @param tag The tag label of the toast notification to be * removed. * @param group The group label of the toast notification to * be removed. */ HRESULT RemoveGroupedTag(const wchar_t *tag, const wchar_t *group); /** * Removes a group of toast notifications, identified by the * specified group label, from action center. * * @param group The group label of the toast notifications to * be removed. */ HRESULT RemoveGroup(const wchar_t *group); /** * Do not call this. Instead, call: * 'DesktopNotificationManagerCompat::get_History()', * to obtain an instance. */ DesktopNotificationHistoryCompat(const wchar_t *, Microsoft::WRL::ComPtr ); private: std::wstring m_aumid; Microsoft::WRL::ComPtr m_history = nullptr; }; swirc-3.5.5/src/NotificationActivator.hpp000066400000000000000000000021731501213070300204510ustar00rootroot00000000000000#ifndef NOTIFICATION_ACTIVATOR_HPP #define NOTIFICATION_ACTIVATOR_HPP #include "ToastActivator.h" #ifndef UNUSED_PARAM #define UNUSED_PARAM(p) ((void) (p)) #endif using namespace ABI::Windows::Data::Xml::Dom; using namespace ABI::Windows::UI::Notifications; using namespace Microsoft::WRL::Wrappers; using namespace Microsoft::WRL; /** * Implement a handler for toast activation, so that when the user * clicks on your toast, your app can do something. (The UUID CLSID * must be unique to your app.) */ class DECLSPEC_UUID("62337340-CB78-4AE9-A524-685424C52DC7") NotificationActivator WrlSealed WrlFinal : public RuntimeClass , INotificationActivationCallback> { public: virtual HRESULT STDMETHODCALLTYPE Activate(LPCWSTR appUserModelId, LPCWSTR invokedArgs, const NOTIFICATION_USER_INPUT_DATA *data, ULONG dataCount) override { /* * TODO: Handle activation */ UNUSED_PARAM(appUserModelId); UNUSED_PARAM(invokedArgs); UNUSED_PARAM(data); UNUSED_PARAM(dataCount); return S_OK; } }; /* * Flag class as COM creatable */ CoCreatableClass(NotificationActivator); #endif swirc-3.5.5/src/README.md000066400000000000000000000000471501213070300147120ustar00rootroot00000000000000# README # Top-level source directory swirc-3.5.5/src/ToastActivator.h000066400000000000000000000106721501213070300165600ustar00rootroot00000000000000 /* this ALWAYS GENERATED file contains the definitions for the interfaces */ /* File created by MIDL compiler version 8.01.0622 */ /* at Tue Jan 19 04:14:07 2038 */ /* Compiler settings for ToastActivator.idl: Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ #pragma warning( disable: 4049 ) /* more than 64k source lines */ /* verify that the version is high enough to compile this file*/ #ifndef __REQUIRED_RPCNDR_H_VERSION__ #define __REQUIRED_RPCNDR_H_VERSION__ 475 #endif #include "rpc.h" #include "rpcndr.h" #ifndef __RPCNDR_H_VERSION__ #error this stub requires an updated version of #endif /* __RPCNDR_H_VERSION__ */ #ifndef COM_NO_WINDOWS_H #include "windows.h" #include "ole2.h" #endif /*COM_NO_WINDOWS_H*/ #ifndef __ToastActivator_h__ #define __ToastActivator_h__ #if defined(_MSC_VER) && (_MSC_VER >= 1020) #pragma once #endif /* Forward Declarations */ #ifndef __INotificationActivationCallback_FWD_DEFINED__ #define __INotificationActivationCallback_FWD_DEFINED__ typedef interface INotificationActivationCallback INotificationActivationCallback; #endif /* __INotificationActivationCallback_FWD_DEFINED__ */ /* header files for imported files */ #include "oaidl.h" #include "ocidl.h" #ifdef __cplusplus extern "C"{ #endif /* interface __MIDL_itf_ToastActivator_0000_0000 */ /* [local] */ typedef struct _NOTIFICATION_USER_INPUT_DATA { LPCWSTR Key; LPCWSTR Value; } NOTIFICATION_USER_INPUT_DATA; extern RPC_IF_HANDLE __MIDL_itf_ToastActivator_0000_0000_v0_0_c_ifspec; extern RPC_IF_HANDLE __MIDL_itf_ToastActivator_0000_0000_v0_0_s_ifspec; #ifndef __INotificationActivationCallback_INTERFACE_DEFINED__ #define __INotificationActivationCallback_INTERFACE_DEFINED__ /* interface INotificationActivationCallback */ /* [ref][uuid][object] */ EXTERN_C const IID IID_INotificationActivationCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("62337340-CB78-4AE9-A524-685424C52DC7") INotificationActivationCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Activate( /* [string][in] */ LPCWSTR appUserModelId, /* [string][in] */ LPCWSTR invokedArgs, /* [unique][size_is][in] */ const NOTIFICATION_USER_INPUT_DATA *data, /* [in] */ ULONG dataCount) = 0; }; #else /* C style interface */ typedef struct INotificationActivationCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( INotificationActivationCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( INotificationActivationCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( INotificationActivationCallback * This); HRESULT ( STDMETHODCALLTYPE *Activate )( INotificationActivationCallback * This, /* [string][in] */ LPCWSTR appUserModelId, /* [string][in] */ LPCWSTR invokedArgs, /* [unique][size_is][in] */ const NOTIFICATION_USER_INPUT_DATA *data, /* [in] */ ULONG dataCount); END_INTERFACE } INotificationActivationCallbackVtbl; interface INotificationActivationCallback { CONST_VTBL struct INotificationActivationCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define INotificationActivationCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define INotificationActivationCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define INotificationActivationCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define INotificationActivationCallback_Activate(This,appUserModelId,invokedArgs,data,dataCount) \ ( (This)->lpVtbl -> Activate(This,appUserModelId,invokedArgs,data,dataCount) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __INotificationActivationCallback_INTERFACE_DEFINED__ */ /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ #ifdef __cplusplus } #endif #endif swirc-3.5.5/src/ToastActivator.idl000066400000000000000000000012731501213070300170760ustar00rootroot00000000000000/* * IDL source for ToastActivator * ============================= * * This file will be processed by the MIDL tool to produce the type * library (ToastActivator.tlb) and marshalling code. */ import "oaidl.idl"; import "ocidl.idl"; typedef struct _NOTIFICATION_USER_INPUT_DATA { LPCWSTR Key; LPCWSTR Value; } NOTIFICATION_USER_INPUT_DATA; [ object, uuid("62337340-CB78-4AE9-A524-685424C52DC7"), pointer_default(ref) ] interface INotificationActivationCallback : IUnknown { HRESULT Activate( [in, string] LPCWSTR appUserModelId, [in, string] LPCWSTR invokedArgs, [in, size_is(dataCount), unique] const NOTIFICATION_USER_INPUT_DATA* data, [in] ULONG dataCount); }; swirc-3.5.5/src/ToastsAPI.cpp000066400000000000000000000150001501213070300157410ustar00rootroot00000000000000/* * Copyright (c) Microsoft. All rights reserved. * This code is licensed under the MIT License (MIT). * * THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE. */ #include "common.h" #include #ifdef HAVE_ATLSTR_H #include #endif #include #include #include #include #include "DesktopNotificationManagerCompat.hpp" #include "ToastsAPI.hpp" #include "errHand.h" #define RETURN_IF_FAILED(hr)\ do {\ HRESULT _hrTemp = hr;\ \ if (FAILED(_hrTemp)) {\ return _hrTemp;\ }\ } while (false) /** * Clear all toasts */ HRESULT Toasts::ClearToasts(void) { std::unique_ptr history; /* * Get the history object * * (Classic Win32 apps MUST use the compat method to obtain * history) */ RETURN_IF_FAILED(DesktopNotificationManagerCompat::get_History (&history)); /* * And clear the toasts... */ return history->Clear(); } /** * Create the toast XML from a template */ #if 0 HRESULT Toasts::CreateToastXml(IXmlDocument **toastXml) { ComPtr doc; const wchar_t toast[] = L"" L"" L"" L"" L"" L"" L"" L"" L""; RETURN_IF_FAILED(DesktopNotificationManagerCompat:: CreateXmlDocumentFromString(&toast[0], &doc)); PCWSTR textValues[] = { L"The", L"universal", L"IRC", L"client!", }; SetTextValues(textValues, ARRAYSIZE(textValues), doc.Get()); return doc.CopyTo(toastXml); } #endif /** * Display the toast using classic COM. Note that is also possible to * create and display the toast using the new C++ /ZW options. */ #if 0 HRESULT Toasts::DisplayToast(void) { ComPtr toastXml; /* * Create the toast content */ RETURN_IF_FAILED(CreateToastXml(&toastXml)); /* * And show it... */ return ShowToast(toastXml.Get()); } #endif static const wchar_t * get_basic_toast() { static const wchar_t str[] = L"" L"" L"" L"" L"" L"" L""; return (&str[0]); } HRESULT Toasts::SendBasicToast(PCWSTR message) { ComPtr doc; RETURN_IF_FAILED(DesktopNotificationManagerCompat:: CreateXmlDocumentFromString(get_basic_toast(), &doc)); PCWSTR textValues[] = { message }; RETURN_IF_FAILED(SetTextValues(textValues, ARRAYSIZE(textValues), doc.Get())); return ShowToast(doc.Get()); } /** * Set the value of the "src" attribute of the "image" node */ #ifdef HAVE_ATLSTR_H HRESULT Toasts::SetImageSrc(PCWSTR imagePath, IXmlDocument *toastXml) { wchar_t imageSrcUri[MAX_PATH] = { 0L }; DWORD size = ARRAYSIZE(imageSrcUri); RETURN_IF_FAILED(::UrlCreateFromPath(imagePath, imageSrcUri, &size, 0)); ComPtr nodeList; RETURN_IF_FAILED(toastXml->GetElementsByTagName (HStringReference(L"image").Get(), &nodeList)); ComPtr imageNode; RETURN_IF_FAILED(nodeList->Item(0, &imageNode)); ComPtr attributes; RETURN_IF_FAILED(imageNode->get_Attributes(&attributes)); ComPtr srcAttribute; RETURN_IF_FAILED(attributes->GetNamedItem (HStringReference(L"src").Get(), &srcAttribute)); return SetNodeValueString(HStringReference(imageSrcUri).Get(), srcAttribute.Get(), toastXml); } #endif HRESULT Toasts::SetNodeValueString(HSTRING inputString, IXmlNode *node, IXmlDocument *xml) { ComPtr inputText; ComPtr inputTextNode; ComPtr appendedChild; RETURN_IF_FAILED(xml->CreateTextNode(inputString, &inputText)); RETURN_IF_FAILED(inputText.As(&inputTextNode)); return node->AppendChild(inputTextNode.Get(), &appendedChild); } /** * Set the values of each of the text nodes */ HRESULT Toasts::SetTextValues(const PCWSTR *textValues, UINT32 textValuesCount, IXmlDocument *toastXml) { ComPtr nodeList; UINT32 nodeListLength; RETURN_IF_FAILED(toastXml->GetElementsByTagName (HStringReference(L"text").Get(), &nodeList)); RETURN_IF_FAILED(nodeList->get_Length(&nodeListLength)); /* * If a template was chosen with fewer text elements, also * change the amount of strings passed to this method. */ RETURN_IF_FAILED(textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG); for (UINT32 i = 0; i < textValuesCount; i++) { ComPtr textNode; RETURN_IF_FAILED(nodeList->Item(i, &textNode)); RETURN_IF_FAILED(SetNodeValueString(HStringReference (textValues[i]).Get(), textNode.Get(), toastXml)); } return S_OK; } /** * Create and display the toast */ HRESULT Toasts::ShowToast(IXmlDocument *xml) { ComPtr notifier; ComPtr toast; /* * Create the notifier * * (Classic Win32 apps MUST use the compat method to create * the notifier) */ RETURN_IF_FAILED(DesktopNotificationManagerCompat::CreateToastNotifier (¬ifier)); /* * And create the notification itself... */ RETURN_IF_FAILED(DesktopNotificationManagerCompat:: CreateToastNotification(xml, &toast)); /* * And show it! */ return notifier->Show(toast.Get()); } static const wchar_t * get_toast() { static const wchar_t toast[] = L"" L"" L"" L"The universal IRC client" L"" L"" L""; return (&toast[0]); } void Toasts::SendTestNotification(void) { ComPtr toast; ComPtr notifier; HRESULT hr; IXmlDocument *doc; hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString (get_toast(), &doc); if (FAILED(hr)) { err_log(0, "CreateXmlDocumentFromString"); return; } hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier); if (FAILED(hr)) { err_log(0, "CreateToastNotifier"); return; } hr = DesktopNotificationManagerCompat::CreateToastNotification(doc, &toast); if (FAILED(hr)) { err_log(0, "CreateToastNotification"); return; } /* * And show it! */ (void) notifier->Show(toast.Get()); } swirc-3.5.5/src/ToastsAPI.hpp000066400000000000000000000021351501213070300157530ustar00rootroot00000000000000/* * Copyright (c) Microsoft. All rights reserved. * This code is licensed under the MIT License (MIT). * * THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE. */ #ifndef TOASTS_API_HPP #define TOASTS_API_HPP #include "NotificationActivator.hpp" namespace Toasts { HRESULT ClearToasts(void); #if 0 HRESULT CreateToastXml(IXmlDocument **); HRESULT DisplayToast(void); #endif HRESULT SendBasicToast(PCWSTR); #ifdef HAVE_ATLSTR_H HRESULT SetImageSrc(PCWSTR, IXmlDocument *); #endif HRESULT SetNodeValueString(HSTRING, IXmlNode *, IXmlDocument *); HRESULT SetTextValues(const PCWSTR *, UINT32, IXmlDocument *); HRESULT ShowToast(IXmlDocument *); void SendTestNotification(void); } #endif swirc-3.5.5/src/assertAPI.c000066400000000000000000000065631501213070300154430ustar00rootroot00000000000000/* Swirc assert API! Copyright (C) 2012-2021 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "assertAPI.h" #include "curses-funcs.h" #include "errHand.h" enum { DNOFILE, DNOFN, DNOASSERTION }; static const char *descriptions[] = { [DNOFILE] = "", [DNOFN] = "", [DNOASSERTION] = "", }; static void assert_doit(const char *, ...) PRINTFLIKE(1); /*lint -printf(1, assert_doit) */ static void assert_doit(const char *fmt, ...) { char out[1000] = { '\0' }; va_list ap; va_start(ap, fmt); #if defined(UNIX) (void) vsnprintf(out, sizeof out, fmt, ap); #elif defined(WIN32) (void) vsnprintf_s(out, sizeof out, _TRUNCATE, fmt, ap); #endif va_end(ap); escape_curses(); (void) fputs(out, stderr); (void) fputc('\n', stderr); } NORETURN void SWAssertFail(const char *file, long int line, const char *fn, const char *assertion) { if (file == NULL || *file == '\0') file = descriptions[DNOFILE]; if (fn == NULL || *fn == '\0') fn = descriptions[DNOFN]; if (assertion == NULL || *assertion == '\0') assertion = descriptions[DNOASSERTION]; assert_doit("%s:%ld: %s: Assertion `%s' failed", file, line, fn, assertion); abort(); } NORETURN void SWAssertPerrorFail(const char *file, long int line, const char *fn, int errnum) { char strerrbuf[MAXERROR] = { '\0' }; if (file == NULL || *file == '\0') file = descriptions[DNOFILE]; if (fn == NULL || *fn == '\0') fn = descriptions[DNOFN]; assert_doit("%s:%ld: %s: Unexpected error: %s", file, line, fn, xstrerror(errnum, strerrbuf, MAXERROR)); abort(); } NORETURN void SWAssertNotReachedFail(const char *file, long int line, const char *fn) { if (file == NULL || *file == '\0') file = descriptions[DNOFILE]; if (fn == NULL || *fn == '\0') fn = descriptions[DNOFN]; assert_doit("%s:%ld: %s: Should not be reached!", file, line, fn); abort(); } swirc-3.5.5/src/assertAPI.h000066400000000000000000000031331501213070300154360ustar00rootroot00000000000000#ifndef ASSERT_API_H #define ASSERT_API_H /*lint -sem(SWAssertFail, r_no) */ /*lint -sem(SWAssertPerrorFail, r_no) */ /*lint -sem(SWAssertNotReachedFail, r_no) */ #ifdef __cplusplus extern "C" { #endif NORETURN void SWAssertFail (const char *file, long int line, const char *fn, const char *assertion); NORETURN void SWAssertPerrorFail (const char *file, long int line, const char *fn, int errnum); NORETURN void SWAssertNotReachedFail (const char *file, long int line, const char *fn); #ifdef __cplusplus } #endif #if defined(UNIX) #define SW_ASSERT_FN __func__ #elif defined(WIN32) #define SW_ASSERT_FN __FUNCTION__ #endif #ifndef NDEBUG #define sw_assert(expr) \ ((expr) \ ? (void) 0 \ : SWAssertFail(__FILE__, __LINE__, SW_ASSERT_FN, #expr)) #define sw_assert_perror(errnum) \ ((errnum == 0) \ ? (void) 0 \ : SWAssertPerrorFail(__FILE__, __LINE__, SW_ASSERT_FN, errnum)) #define sw_assert_not_reached() \ SWAssertNotReachedFail(__FILE__, __LINE__, SW_ASSERT_FN) #endif #ifdef NDEBUG #define sw_assert(expr) ((void) 0) #define sw_assert_perror(errnum) ((void) 0) #define sw_assert_not_reached() SWAssertNotReachedFail(__FILE__, __LINE__, SW_ASSERT_FN) #endif /* * Define sw_static_assert() */ #if defined(WIN32) || (defined(__cplusplus) && __cplusplus >= 201103L) #define sw_static_assert(expr, msg) \ static_assert((expr), msg) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #define sw_static_assert(expr, msg) \ _Static_assert((expr), msg) #else #define sw_static_assert(expr, msg) ((void) 0) #endif #endif swirc-3.5.5/src/b64_decode.c000066400000000000000000000261521501213070300155020ustar00rootroot00000000000000/* * Copyright (c) 1996, 1998 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #define Assert(Cond) ((void) 0) static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ static int b64rmap_initialized = 0; static uint8_t b64rmap[256]; static const uint8_t b64rmap_special = 0xf0; static const uint8_t b64rmap_end = 0xfd; static const uint8_t b64rmap_space = 0xfe; static const uint8_t b64rmap_invalid = 0xff; /** * Initializing the reverse map is not thread safe. * Which is fine for NSD. For now... **/ static void b64_initialize_rmap (void) { int i; /* Null: end of string, stop parsing */ b64rmap[0] = b64rmap_end; for (i = 1; i < 256; ++i) { /* Whitespaces */ if (isspace(i)) b64rmap[i] = b64rmap_space; /* Padding: stop parsing */ else if (i == (unsigned char)Pad64) b64rmap[i] = b64rmap_end; /* Non-base64 char */ else b64rmap[i] = b64rmap_invalid; } /* Fill reverse mapping for base64 chars */ for (i = 0; Base64[i] != '\0'; ++i) b64rmap[(uint8_t)Base64[i]] = i; b64rmap_initialized = 1; } static int b64_decode_do(unsigned char const *src, uint8_t *target, size_t targsize) { int tarindex, state, ch; uint8_t ofs; state = 0; tarindex = 0; while (1) { ch = *src++; ofs = b64rmap[ch]; if (ofs >= b64rmap_special) { /* Ignore whitespaces */ if (ofs == b64rmap_space) continue; /* End of base64 characters */ if (ofs == b64rmap_end) break; /* A non-base64 character. */ return (-1); } switch (state) { case 0: if ((size_t)tarindex >= targsize) return (-1); target[tarindex] = ofs << 2; state = 1; break; case 1: if ((size_t)tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 4; target[tarindex+1] = (ofs & 0x0f) << 4 ; tarindex++; state = 2; break; case 2: if ((size_t)tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 2; target[tarindex+1] = (ofs & 0x03) << 6; tarindex++; state = 3; break; case 3: if ((size_t)tarindex >= targsize) return (-1); target[tarindex] |= ofs; tarindex++; state = 0; break; default: abort(); } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for ((void)NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for ((void)NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) return (-1); /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target[tarindex] != 0) return (-1); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } static int b64_decode_len(unsigned char const *src) { int tarindex, state, ch; uint8_t ofs; state = 0; tarindex = 0; while (1) { ch = *src++; ofs = b64rmap[ch]; if (ofs >= b64rmap_special) { /* Ignore whitespaces */ if (ofs == b64rmap_space) continue; /* End of base64 characters */ if (ofs == b64rmap_end) break; /* A non-base64 character. */ return (-1); } switch (state) { case 0: state = 1; break; case 1: tarindex++; state = 2; break; case 2: tarindex++; state = 3; break; case 3: tarindex++; state = 0; break; default: abort(); } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for ((void)NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for ((void)NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) return (-1); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } int b64_decode(char const *src, uint8_t *target, size_t targsize) { if (!b64rmap_initialized) b64_initialize_rmap (); if (target) return b64_decode_do ((unsigned char*)src, target, targsize); else return b64_decode_len ((unsigned char*)src); } swirc-3.5.5/src/b64_encode.c000066400000000000000000000162571501213070300155210ustar00rootroot00000000000000/* * Copyright (c) 1996, 1998 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #define Assert(Cond) ((void) 0) static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ int size_to_int(const size_t); /* * 2016-01-01 Markus: This routine was originally of name b64_ntop. */ int b64_encode(uint8_t const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; size_t i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); Assert(output[3] < 64); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength == 1) target[datalength++] = Pad64; else target[datalength++] = Base64[output[2]]; target[datalength++] = Pad64; } if (datalength >= targsize) return (-1); target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (size_to_int(datalength)); } swirc-3.5.5/src/build.mk000066400000000000000000000056551501213070300150750ustar00rootroot00000000000000SRC_DIR := src/ TGTS += swirc OBJS += $(SRC_DIR)assertAPI.o\ $(SRC_DIR)b64_decode.o\ $(SRC_DIR)b64_encode.o\ $(SRC_DIR)config.o\ $(SRC_DIR)crypt.o\ $(SRC_DIR)curses-funcs.o\ $(SRC_DIR)cursesInit.o\ $(SRC_DIR)dataClassify.o\ $(SRC_DIR)errHand.o\ $(SRC_DIR)filePred.o\ $(SRC_DIR)get_x509_fp.o\ $(SRC_DIR)icb.o\ $(SRC_DIR)identd-unix.o\ $(SRC_DIR)identd.o\ $(SRC_DIR)initcolors.o\ $(SRC_DIR)interpreter.o\ $(SRC_DIR)io-loop.o\ $(SRC_DIR)irc.o\ $(SRC_DIR)libUtils.o\ $(SRC_DIR)log.o\ $(SRC_DIR)main.o\ $(SRC_DIR)messagetags.o\ $(SRC_DIR)nestHome.o\ $(SRC_DIR)net-unix.o\ $(SRC_DIR)netsplit.o\ $(SRC_DIR)network-openssl.o\ $(SRC_DIR)network.o\ $(SRC_DIR)nicklist.o\ $(SRC_DIR)options.o\ $(SRC_DIR)ossl-scripts.o\ $(SRC_DIR)printtext.o\ $(SRC_DIR)pthrMutex.o\ $(SRC_DIR)readline.o\ $(SRC_DIR)readlineAPI.o\ $(SRC_DIR)readlineTabCompletion.o\ $(SRC_DIR)sig-unix.o\ $(SRC_DIR)socks.o\ $(SRC_DIR)spell.o\ $(SRC_DIR)squeeze_text_deco.o\ $(SRC_DIR)statusbar.o\ $(SRC_DIR)strHand.o\ $(SRC_DIR)strcasestr.o\ $(SRC_DIR)strcat.o\ $(SRC_DIR)strcpy.o\ $(SRC_DIR)strdup_printf.o\ $(SRC_DIR)strnlen.o\ $(SRC_DIR)term-unix.o\ $(SRC_DIR)terminal.o\ $(SRC_DIR)textBuffer.o\ $(SRC_DIR)theme.o\ $(SRC_DIR)titlebar.o\ $(SRC_DIR)tls-server-unix.o\ $(SRC_DIR)tls-server.o\ $(SRC_DIR)wcscat.o\ $(SRC_DIR)wcscpy.o\ $(SRC_DIR)window.o\ $(SRC_DIR)x509_check_host.o SRCS = $(SRC_DIR)assertAPI.c\ $(SRC_DIR)b64_decode.c\ $(SRC_DIR)b64_encode.c\ $(SRC_DIR)config.cpp\ $(SRC_DIR)crypt.cpp\ $(SRC_DIR)curses-funcs.c\ $(SRC_DIR)cursesInit.c\ $(SRC_DIR)dataClassify.c\ $(SRC_DIR)errHand.c\ $(SRC_DIR)filePred.c\ $(SRC_DIR)get_x509_fp.cpp\ $(SRC_DIR)icb.c\ $(SRC_DIR)identd-unix.cpp\ $(SRC_DIR)identd.cpp\ $(SRC_DIR)initcolors.c\ $(SRC_DIR)interpreter.cpp\ $(SRC_DIR)io-loop.c\ $(SRC_DIR)irc.c\ $(SRC_DIR)libUtils.c\ $(SRC_DIR)log.c\ $(SRC_DIR)main.cpp\ $(SRC_DIR)messagetags.c\ $(SRC_DIR)nestHome.c\ $(SRC_DIR)net-unix.c\ $(SRC_DIR)netsplit.cpp\ $(SRC_DIR)network-openssl.c\ $(SRC_DIR)network.cpp\ $(SRC_DIR)nicklist.cpp\ $(SRC_DIR)options.c\ $(SRC_DIR)ossl-scripts.c\ $(SRC_DIR)printtext.cpp\ $(SRC_DIR)pthrMutex.c\ $(SRC_DIR)readline.c\ $(SRC_DIR)readlineAPI.c\ $(SRC_DIR)readlineTabCompletion.c\ $(SRC_DIR)sig-unix.c\ $(SRC_DIR)socks.cpp\ $(SRC_DIR)spell.cpp\ $(SRC_DIR)squeeze_text_deco.cpp\ $(SRC_DIR)statusbar.cpp\ $(SRC_DIR)strHand.c\ $(SRC_DIR)strcasestr.c\ $(SRC_DIR)strcat.c\ $(SRC_DIR)strcpy.c\ $(SRC_DIR)strdup_printf.c\ $(SRC_DIR)strnlen.c\ $(SRC_DIR)term-unix.c\ $(SRC_DIR)terminal.c\ $(SRC_DIR)textBuffer.c\ $(SRC_DIR)theme.c\ $(SRC_DIR)titlebar.c\ $(SRC_DIR)tls-server-unix.cpp\ $(SRC_DIR)tls-server.cpp\ $(SRC_DIR)wcscat.c\ $(SRC_DIR)wcscpy.c\ $(SRC_DIR)window.c\ $(SRC_DIR)x509_check_host.c $(SRC_DIR)include/swircpaths.h: ./gen-hdr.sh "$(PREFIX)" swirc: $(SRC_DIR)include/swircpaths.h $(OBJS) $(E) " LINK " $@ $(Q) $(CXX) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) # $(Q) strip $@ swirc-3.5.5/src/build.w32.mk000066400000000000000000000041271501213070300155000ustar00rootroot00000000000000SRC_DIR = src/ TGTS = $(TGTS) swirc.exe OBJS = $(OBJS)\ $(SRC_DIR)DesktopNotificationManagerCompat.obj\ $(SRC_DIR)ToastsAPI.obj\ $(SRC_DIR)assertAPI.obj\ $(SRC_DIR)b64_decode.obj\ $(SRC_DIR)b64_encode.obj\ $(SRC_DIR)config.obj\ $(SRC_DIR)crypt.obj\ $(SRC_DIR)curses-funcs.obj\ $(SRC_DIR)cursesInit.obj\ $(SRC_DIR)dataClassify.obj\ $(SRC_DIR)errHand.obj\ $(SRC_DIR)filePred.obj\ $(SRC_DIR)get_x509_fp.obj\ $(SRC_DIR)icb.obj\ $(SRC_DIR)identd-w32.obj\ $(SRC_DIR)identd.obj\ $(SRC_DIR)init_once.obj\ $(SRC_DIR)initcolors.obj\ $(SRC_DIR)interpreter.obj\ $(SRC_DIR)io-loop.obj\ $(SRC_DIR)irc.obj\ $(SRC_DIR)libUtils.obj\ $(SRC_DIR)log.obj\ $(SRC_DIR)main.obj\ $(SRC_DIR)messagetags.obj\ $(SRC_DIR)nestHome.obj\ $(SRC_DIR)net-w32.obj\ $(SRC_DIR)netsplit.obj\ $(SRC_DIR)network-openssl.obj\ $(SRC_DIR)network.obj\ $(SRC_DIR)nicklist.obj\ $(SRC_DIR)options.obj\ $(SRC_DIR)ossl-scripts.obj\ $(SRC_DIR)printtext.obj\ $(SRC_DIR)readline.obj\ $(SRC_DIR)readlineAPI.obj\ $(SRC_DIR)readlineTabCompletion.obj\ $(SRC_DIR)sig-w32.obj\ $(SRC_DIR)socks.obj\ $(SRC_DIR)spell.obj\ $(SRC_DIR)squeeze_text_deco.obj\ $(SRC_DIR)statusbar.obj\ $(SRC_DIR)strHand.obj\ $(SRC_DIR)strcasestr.obj\ $(SRC_DIR)strcat.obj\ $(SRC_DIR)strcpy.obj\ $(SRC_DIR)strdup_printf.obj\ $(SRC_DIR)strnlen.obj\ $(SRC_DIR)term-w32.obj\ $(SRC_DIR)terminal.obj\ $(SRC_DIR)textBuffer.obj\ $(SRC_DIR)theme.obj\ $(SRC_DIR)titlebar.obj\ $(SRC_DIR)tls-server-w32.obj\ $(SRC_DIR)tls-server.obj\ $(SRC_DIR)vcMutex.obj\ $(SRC_DIR)wcscat.obj\ $(SRC_DIR)wcscpy.obj\ $(SRC_DIR)window.obj\ $(SRC_DIR)x509_check_host.obj swirc.exe: fetch_and_expand $(OBJS) rc -foswirc.res -v $(SRC_DIR)swirc.rc $(CC) -Feswirc $(OBJS) swirc.res -link $(LDFLAGS) $(LDLIBS) fetch_and_expand: cscript $(SRC_DIR)get_file.js expand curl-$(CURL_VERSION).cab "-F:*" . expand gnu-bundle-$(GNU_BUNDLE_DATE).cab "-F:*" . expand hunspell-$(HUNSPELL_VERSION).cab "-F:*" . expand hunspell-en-us.cab "-F:*" . expand libressl-$(LIBRESSL_VERSION).cab "-F:*" . expand pdcurses-$(PDCURSES_VERSION).cab "-F:*" . expand swirc-locales-$(LOCALES_SNAP).cab "-F:*" . swirc-3.5.5/src/colors.c000066400000000000000000000155501501213070300151050ustar00rootroot00000000000000/* Copyright (c) 2020 Markus Uhlin All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // Dump extended IRC color palette in Curses RGB scale (0-1000) converted // from base 16 suitable for init_color(). // // Compile with: cc -O2 -Wall -lm -pipe colors.c #include #include #include #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif typedef struct { short int r; short int g; short int b; } rgb_t; static struct { short int num; rgb_t val; } colors[] = { /* 16-27 */ { 52, {0x47,0x00,0x00} }, { 94, {0x47,0x21,0x00} }, { 100, {0x47,0x47,0x00} }, { 58, {0x32,0x47,0x00} }, { 22, {0x00,0x47,0x00} }, { 29, {0x00,0x47,0x2c} }, { 23, {0x00,0x47,0x47} }, { 24, {0x00,0x27,0x47} }, { 17, {0x00,0x00,0x47} }, { 54, {0x2e,0x00,0x47} }, { 53, {0x47,0x00,0x47} }, { 89, {0x47,0x00,0x2a} }, /* 28-39 */ { 88, {0x74,0x00,0x00} }, { 130, {0x74,0x3a,0x00} }, { 142, {0x74,0x74,0x00} }, { 64, {0x51,0x74,0x00} }, { 28, {0x00,0x74,0x00} }, { 35, {0x00,0x74,0x49} }, { 30, {0x00,0x74,0x74} }, { 25, {0x00,0x40,0x74} }, { 18, {0x00,0x00,0x74} }, { 91, {0x4b,0x00,0x74} }, { 90, {0x74,0x00,0x74} }, { 125, {0x74,0x00,0x45} }, /* 40-51 */ { 124, {0xb5,0x00,0x00} }, { 166, {0xb5,0x63,0x00} }, { 184, {0xb5,0xb5,0x00} }, { 106, {0x7d,0xb5,0x00} }, { 34, {0x00,0xb5,0x00} }, { 49, {0x00,0xb5,0x71} }, { 37, {0x00,0xb5,0xb5} }, { 33, {0x00,0x63,0xb5} }, { 19, {0x00,0x00,0xb5} }, { 129, {0x75,0x00,0xb5} }, { 127, {0xb5,0x00,0xb5} }, { 161, {0xb5,0x00,0x6b} }, /* 52-63 */ { 196, {0xff,0x00,0x00} }, { 208, {0xff,0x8c,0x00} }, { 226, {0xff,0xff,0x00} }, { 154, {0xb2,0xff,0x00} }, { 46, {0x00,0xff,0x00} }, { 86, {0x00,0xff,0xa0} }, { 51, {0x00,0xff,0xff} }, { 75, {0x00,0x8c,0xff} }, { 21, {0x00,0x00,0xff} }, { 171, {0xa5,0x00,0xff} }, { 201, {0xff,0x00,0xff} }, { 198, {0xff,0x00,0x98} }, /* 64-75 */ { 203, {0xff,0x59,0x59} }, { 215, {0xff,0xb4,0x59} }, { 227, {0xff,0xff,0x71} }, { 191, {0xcf,0xff,0x60} }, { 83, {0x6f,0xff,0x6f} }, { 122, {0x65,0xff,0xc9} }, { 87, {0x6d,0xff,0xff} }, { 111, {0x59,0xb4,0xff} }, { 63, {0x59,0x59,0xff} }, { 177, {0xc4,0x59,0xff} }, { 207, {0xff,0x66,0xff} }, { 205, {0xff,0x59,0xbc} }, /* 76-87 */ { 217, {0xff,0x9c,0x9c} }, { 223, {0xff,0xd3,0x9c} }, { 229, {0xff,0xff,0x9c} }, { 193, {0xe2,0xff,0x9c} }, { 157, {0x9c,0xff,0x9c} }, { 158, {0x9c,0xff,0xdb} }, { 159, {0x9c,0xff,0xff} }, { 153, {0x9c,0xd3,0xff} }, { 147, {0x9c,0x9c,0xff} }, { 183, {0xdc,0x9c,0xff} }, { 219, {0xff,0x9c,0xff} }, { 212, {0xff,0x94,0xd3} }, /* 88-98 */ { 16, {0x00,0x00,0x00} }, { 233, {0x13,0x13,0x13} }, { 235, {0x28,0x28,0x28} }, { 237, {0x36,0x36,0x36} }, { 239, {0x4d,0x4d,0x4d} }, { 241, {0x65,0x65,0x65} }, { 244, {0x81,0x81,0x81} }, { 247, {0x9f,0x9f,0x9f} }, { 250, {0xbc,0xbc,0xbc} }, { 254, {0xe2,0xe2,0xe2} }, { 231, {0xff,0xff,0xff} }, }; static long int getval(short int i) { const double input_start = 0.0; const double input_end = 255.0; const double input = (double) i; const double output_start = 0.0; const double output_end = 1000.0; const double out = output_start + ((output_end - output_start) / (input_end - input_start)) * (input - input_start); return lround(out); } static char * rgb(short int r, short int g, short int b) { static char out[128] = { '\0' }; const int ret = snprintf(out, sizeof out, "%ld,%ld,%ld", getval(r), getval(g), getval(b)); if (ret == -1 || ret >= sizeof out) { fputs("rgb: snprintf error\n", stderr); exit(1); } return (&out[0]); } static const char * get_spaces(const short int color) { if (color < 10) return " "; else if (color < 100) return " "; return " "; } static void dump_struct(FILE *fp) { fputs("typedef struct {\n", fp); fputs(" short int r;\n", fp); fputs(" short int g;\n", fp); fputs(" short int b;\n", fp); fputs("} rgb_t;\n\n", fp); fputs("static struct {\n", fp); fputs(" short int color;\n", fp); fputs(" rgb_t val;\n", fp); fputs("} ext_colors_rgb[] = {\n", fp); for (size_t i = 0; i < nitems(colors); i++) { switch (colors[i].num) { case 52: fprintf(fp, " /* 16-27 */\n"); break; case 88: fprintf(fp, "\n /* 28-39 */\n"); break; case 124: fprintf(fp, "\n /* 40-51 */\n"); break; case 196: fprintf(fp, "\n /* 52-63 */\n"); break; case 203: fprintf(fp, "\n /* 64-75 */\n"); break; case 217: fprintf(fp, "\n /* 76-87 */\n"); break; case 16: fprintf(fp, "\n /* 88-98 */\n"); break; } rgb_t current = colors[i].val; fprintf(fp, " { %hd,%s{%s} },\n", colors[i].num, get_spaces(colors[i].num), rgb(current.r,current.g,current.b)); } fputs("};\n", fp); } int main() { printf("0\tWhite\t\t(%s)\n", rgb(255,255,255)); printf("1\tBlack\t\t(%s)\n", rgb(0,0,0)); printf("2\tBlue\t\t(%s)\n", rgb(0,0,127)); printf("3\tGreen\t\t(%s)\n", rgb(0,147,0)); printf("4\tLight Red\t(%s)\n", rgb(255,0,0)); printf("5\tBrown\t\t(%s)\n", rgb(127,0,0)); printf("6\tPurple\t\t(%s)\n", rgb(156,0,156)); printf("7\tOrange\t\t(%s)\n", rgb(252,127,0)); printf("8\tYellow\t\t(%s)\n", rgb(255,255,0)); printf("9\tLight Green\t(%s)\n", rgb(0,252,0)); printf("10\tCyan\t\t(%s)\n", rgb(0,147,147)); printf("11\tLight Cyan\t(%s)\n", rgb(0,255,255)); printf("12\tLight Blue\t(%s)\n", rgb(0,0,252)); printf("13\tPink\t\t(%s)\n", rgb(255,0,255)); printf("14\tGrey\t\t(%s)\n", rgb(127,127,127)); printf("15\tLight Grey\t(%s)\n", rgb(210,210,210)); FILE *fp = NULL; const char path[] = "/tmp/struct.c"; if ((fp = fopen(path, "w")) == NULL) { perror("main: fopen"); return 1; } else { dump_struct(fp); } if (fp) fclose(fp); printf("struct placed in %s\n", path); return 0; } swirc-3.5.5/src/commands/000077500000000000000000000000001501213070300152335ustar00rootroot00000000000000swirc-3.5.5/src/commands/README.md000066400000000000000000000001271501213070300165120ustar00rootroot00000000000000# README # The files within this folder contains sources of **Swirc** slash-commands. swirc-3.5.5/src/commands/admin.cpp000066400000000000000000000075011501213070300170320ustar00rootroot00000000000000/* Administrative commands Copyright (C) 2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../irc.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "admin.h" /* * usage: /die [--I-am-sure] */ void cmd_die(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (strings_match(data, "")) printtext_print("err", "missing args"); else if (!strings_match(data, "--I-am-sure")) printtext_print("err", "aborting..."); else if (net_send("DIE") < 0) printtext_print("err", "cannot send"); else printtext_print("success", "msg sent"); } /* * usage: /gline [ :] */ void cmd_gline(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (strings_match(data, "")) printtext_print("err", "missing args"); else if (net_send("GLINE %s", data) < 0) printtext_print("err", "cannot send"); else return; } /* * usage: /kline [ :] */ void cmd_kline(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (strings_match(data, "")) printtext_print("err", "missing args"); else if (net_send("KLINE %s", data) < 0) printtext_print("err", "cannot send"); else return; } /* * usage: /rehash */ void cmd_rehash(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (!strings_match(data, "")) printtext_print("err", "implicit trailing data"); else if (net_send("REHASH") < 0) printtext_print("err", "cannot send"); else printtext_print("success", "msg sent"); } /* * usage: /restart [--I-am-sure] */ void cmd_restart(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (strings_match(data, "")) printtext_print("err", "missing args"); else if (!strings_match(data, "--I-am-sure")) printtext_print("err", "aborting..."); else if (net_send("RESTART") < 0) printtext_print("err", "cannot send"); else printtext_print("success", "msg sent"); } /* * usage: /wallops */ void cmd_wallops(CSTRING data) { if (!g_am_irc_op) printtext_print("err", "not irc op"); else if (strings_match(data, "")) printtext_print("err", "missing args"); else if (net_send("WALLOPS %s", data) < 0) printtext_print("err", "cannot send"); else printtext_print("success", "wallops msg sent: %s", data); } swirc-3.5.5/src/commands/admin.h000066400000000000000000000003621501213070300164750ustar00rootroot00000000000000#ifndef CMDS_ADMIN_H #define CMDS_ADMIN_H __SWIRC_BEGIN_DECLS void cmd_die(CSTRING); void cmd_gline(CSTRING); void cmd_kline(CSTRING); void cmd_rehash(CSTRING); void cmd_restart(CSTRING); void cmd_wallops(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/announce.cpp000066400000000000000000000223501501213070300175470ustar00rootroot00000000000000/* Send announcements on IRC Copyright (C) 2024-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include #include "../assertAPI.h" #include "../dataClassify.h" #include "../io-loop.h" #include "../irc.h" #include "../libUtils.h" #include "../main.h" #include "../printtext.h" #include "../strHand.h" #include "announce.h" #include "znc.h" class announce { public: std::vector chans; std::string msg; announce(); announce(bool, CSTRING, CSTRING); void print(void); void send(void); private: bool znc_broadcast; }; announce::announce() { this->msg.assign(""); this->znc_broadcast = false; } announce::announce(bool p_znc_broadcast, CSTRING p_chans, CSTRING p_msg) { CSTRING token; STRING str; STRING last = const_cast(""); static chararray_t sep = ","; str = sw_strdup(p_chans); if ((token = strtok_r(str, sep, &last)) == nullptr) sw_assert_not_reached(); if (strpbrk(token, g_forbidden_chan_name_chars) == nullptr) this->chans.push_back(token); while ((token = strtok_r(nullptr, sep, &last)) != nullptr) { if (strpbrk(token, g_forbidden_chan_name_chars) == nullptr) this->chans.push_back(token); } free(str); this->msg.assign(p_msg); this->znc_broadcast = p_znc_broadcast; } void announce::print(void) { std::string chanlist(""); for (const std::string &str : this->chans) { if (!is_irc_channel(str.c_str())) chanlist.append("#"); chanlist.append(str).append(" "); } #ifndef _lint if (!chanlist.empty()) chanlist.pop_back(); #endif printtext_print("sp1", "channels: %s", (this->chans.empty() ? "" : chanlist.c_str())); printtext_print("sp1", "message: %s", this->msg.c_str()); printtext_print("sp1", "znc broadcast: %s", (this->znc_broadcast ? "yes" : "no")); printtext_print("sp1", " "); } void announce::send(void) { if (this->chans.empty()) { printtext_print("err", "the announcement has no channels"); return; } for (const std::string &str : this->chans) { std::string winlabel(str); if (!is_irc_channel(winlabel.c_str())) winlabel.insert(0, "#"); if (window_by_label(winlabel.c_str()) != nullptr) { transmit_user_input(winlabel.c_str(), this->msg.c_str()); } } std::string arg("Broadcast"); arg.append(" ").append(this->msg); if (this->znc_broadcast) cmd_znc(arg.c_str()); } static const uint32_t MAXCHANNELS = 300; static const uint32_t MAXMESSAGE = 550; static const uint32_t MAXANNOUNCEMENTS = 30; static std::vector announcements; static bool subcmd_ok(CSTRING subcmd) { if (strings_match(subcmd, "doit")) return true; else if (strings_match(subcmd, "list")) return true; else if (strings_match(subcmd, "new")) return true; else if (strings_match(subcmd, "rm")) return true; return false; } static void subcmd_doit(CSTRING arg) { long int no; long int val = 0; std::vector::iterator it; if (arg == nullptr) { printtext_print("err", "%s: insufficient args", __func__); return; } else if (announcements.empty()) { printtext_print("err", "%s: zero announcements", __func__); return; } else if (!is_numeric(arg)) { printtext_print("err", "%s: bad argument", __func__); return; } else if (!getval_strtol(arg, 0, size_to_long(__func__, announcements.size() - 1), &val)) { printtext_print("err", "%s: bad number", __func__); return; } no = 0; it = announcements.begin(); while (it != announcements.end()) { if (no == val) { printtext_print("success", "sending the announcement" "..."); (*it).send(); return; } ++no; ++it; } } static void subcmd_list(void) { long int no; std::vector::iterator it; if (announcements.empty()) { printtext_print("err", "%s: zero announcements", __func__); return; } no = 0; it = announcements.begin(); while (it != announcements.end()) { printtext_print("sp1", "----- announcement %ld -----", no); (*it).print(); ++no; ++it; } } static void subcmd_new(CSTRING arg1, CSTRING arg2, CSTRING arg3) { bool znc_broadcast; if (arg1 == nullptr || arg2 == nullptr || arg3 == nullptr) { printtext_print("err", "%s: insufficient args", __func__); return; } else if (!strings_match(arg1, "yes") && !strings_match(arg1, "no")) { printtext_print("err", "%s: what? yes or no?", __func__); return; } else if (strings_match(arg1, "yes")) { znc_broadcast = true; } else if (strings_match(arg1, "no")) { znc_broadcast = false; } else { sw_assert_not_reached(); } if (strings_match(arg2, "")) { printtext_print("err", "%s: no channels", __func__); return; } else if (strings_match(arg3, "")) { printtext_print("err", "%s: no message", __func__); return; } else if (!(announcements.size() < MAXANNOUNCEMENTS)) { printtext_print("err", "%s: too many announcements", __func__); return; } else if (strlen(arg2) > MAXCHANNELS) { printtext_print("err", "%s: too many channels", __func__); return; } else if (strlen(arg3) > MAXMESSAGE) { printtext_print("err", "%s: too long message", __func__); return; } try { announce obj(znc_broadcast, arg2, arg3); #if defined(__cplusplus) && __cplusplus >= 201103L announcements.emplace_back(obj); #else announcements.push_back(obj); #endif } catch (...) { printtext_print("err", "%s: error while creating the " "announcement", __func__); return; } printtext_print("success", "ok"); } static void subcmd_rm(CSTRING arg) { if (arg == nullptr) { printtext_print("err", "%s: insufficient args", __func__); return; } else if (announcements.empty()) { printtext_print("err", "%s: zero announcements", __func__); return; } else if (is_numeric(arg)) { long int num = 0; if (!getval_strtol(arg, 0, size_to_long(__func__, announcements.size() - 1), &num)) { printtext_print("err", "%s: bad number", __func__); return; } try { announcements.erase(announcements.begin() + num); } catch (...) { printtext_print("err", "%s: failed to remove " "element %ld", __func__, num); return; } printtext_print("success", "ok"); } else if (strings_match(arg, "all")) { announcements.clear(); printtext_print("success", "ok"); } else { printtext_print("err", "%s: bad argument", __func__); } } /* * usage: * /announce [doit|list|new|rm] [args] * /announce doit <#> * /announce list * /announce new <[yes|no]> * /announce rm <[#|all]> */ void cmd_announce(CSTRING data) { CSTRING arg[3]; CSTRING subcmd; STRING dcopy; STRING last = const_cast(""); static chararray_t cmd = "/announce"; static chararray_t sep = "\n"; if (strings_match(data, "")) { subcmd_list(); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 3); if ((subcmd = strtok_r(dcopy, sep, &last)) == nullptr) { printf_and_free(dcopy, "%s: insufficient args", cmd); return; } else if (!subcmd_ok(subcmd)) { printf_and_free(dcopy, "%s: invalid subcommand '%s'", cmd, subcmd); return; } arg[0] = strtok_r(nullptr, sep, &last); arg[1] = strtok_r(nullptr, sep, &last); arg[2] = strtok_r(nullptr, sep, &last); try { if (strings_match(subcmd, "doit")) subcmd_doit(arg[0]); else if (strings_match(subcmd, "list")) subcmd_list(); else if (strings_match(subcmd, "new")) subcmd_new(arg[0], arg[1], arg[2]); else if (strings_match(subcmd, "rm")) subcmd_rm(arg[0]); else printtext_print("err", "%s: invalid subcommand", cmd); } catch (const std::exception &ex) { printtext_print("err", "%s: exception: %s", cmd, ex.what()); } free(dcopy); } long int size_to_long(CSTRING fn, const size_t size) { if (size > LONG_MAX) { char buf[200] = { '\0' }; (void)snprintf(buf, sizeof buf, "%s: long maximum exceeded", fn); throw std::overflow_error(&buf[0]); } return static_cast(size); } swirc-3.5.5/src/commands/announce.h000066400000000000000000000002741501213070300172150ustar00rootroot00000000000000#ifndef SRC_COMMANDS_ANNOUNCE_H_ #define SRC_COMMANDS_ANNOUNCE_H_ __SWIRC_BEGIN_DECLS void cmd_announce(CSTRING); long int size_to_long(CSTRING, const size_t); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/ban.c000066400000000000000000000047441501213070300161500ustar00rootroot00000000000000/* Command ban and unban Copyright (C) 2019-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "ban.h" static const char err1[] = "no mask"; static const char err2[] = "active window isn't an irc channel"; /* * usage: /ban */ void cmd_ban(const char *data) { static const char cmd[] = "/ban"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err2); return; } (void) net_send("MODE %s +b %s", ACTWINLABEL, data); } /* * usage: /unban */ void cmd_unban(const char *data) { static const char cmd[] = "/unban"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err2); return; } (void) net_send("MODE %s -b %s", ACTWINLABEL, data); } swirc-3.5.5/src/commands/ban.h000066400000000000000000000002151501213070300161420ustar00rootroot00000000000000#ifndef CMD_BAN_H #define CMD_BAN_H __SWIRC_BEGIN_DECLS void cmd_ban(const char *); void cmd_unban(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/build.mk000066400000000000000000000036401501213070300166660ustar00rootroot00000000000000COMMANDS_DIR := src/commands/ COMMANDS_SRCS = $(COMMANDS_DIR)admin.cpp\ $(COMMANDS_DIR)announce.cpp\ $(COMMANDS_DIR)ban.c\ $(COMMANDS_DIR)cap.cpp\ $(COMMANDS_DIR)cleartoasts.cpp\ $(COMMANDS_DIR)colormap.cpp\ $(COMMANDS_DIR)connect.c\ $(COMMANDS_DIR)ctcp.c\ $(COMMANDS_DIR)dcc-unix.cpp\ $(COMMANDS_DIR)dcc.cpp\ $(COMMANDS_DIR)echo.c\ $(COMMANDS_DIR)fetchdic.cpp\ $(COMMANDS_DIR)ftp-unix.cpp\ $(COMMANDS_DIR)ftp.cpp\ $(COMMANDS_DIR)ignore.cpp\ $(COMMANDS_DIR)info.cpp\ $(COMMANDS_DIR)invite.c\ $(COMMANDS_DIR)jp.cpp\ $(COMMANDS_DIR)kick.c\ $(COMMANDS_DIR)me.c\ $(COMMANDS_DIR)misc.c\ $(COMMANDS_DIR)msg.c\ $(COMMANDS_DIR)nick.c\ $(COMMANDS_DIR)notice.cpp\ $(COMMANDS_DIR)op.c\ $(COMMANDS_DIR)sasl-scram-sha.cpp\ $(COMMANDS_DIR)sasl.cpp\ $(COMMANDS_DIR)say.c\ $(COMMANDS_DIR)services.cpp\ $(COMMANDS_DIR)servlist.cpp\ $(COMMANDS_DIR)squery.cpp\ $(COMMANDS_DIR)theme.c\ $(COMMANDS_DIR)topic.c\ $(COMMANDS_DIR)voice.c\ $(COMMANDS_DIR)wholeft.cpp\ $(COMMANDS_DIR)znc.cpp OBJS = $(COMMANDS_DIR)admin.o\ $(COMMANDS_DIR)announce.o\ $(COMMANDS_DIR)ban.o\ $(COMMANDS_DIR)cap.o\ $(COMMANDS_DIR)cleartoasts.o\ $(COMMANDS_DIR)colormap.o\ $(COMMANDS_DIR)connect.o\ $(COMMANDS_DIR)ctcp.o\ $(COMMANDS_DIR)dcc-unix.o\ $(COMMANDS_DIR)dcc.o\ $(COMMANDS_DIR)echo.o\ $(COMMANDS_DIR)fetchdic.o\ $(COMMANDS_DIR)ftp-unix.o\ $(COMMANDS_DIR)ftp.o\ $(COMMANDS_DIR)ignore.o\ $(COMMANDS_DIR)info.o\ $(COMMANDS_DIR)invite.o\ $(COMMANDS_DIR)jp.o\ $(COMMANDS_DIR)kick.o\ $(COMMANDS_DIR)me.o\ $(COMMANDS_DIR)misc.o\ $(COMMANDS_DIR)msg.o\ $(COMMANDS_DIR)nick.o\ $(COMMANDS_DIR)notice.o\ $(COMMANDS_DIR)op.o\ $(COMMANDS_DIR)sasl-scram-sha.o\ $(COMMANDS_DIR)sasl.o\ $(COMMANDS_DIR)say.o\ $(COMMANDS_DIR)services.o\ $(COMMANDS_DIR)servlist.o\ $(COMMANDS_DIR)squery.o\ $(COMMANDS_DIR)theme.o\ $(COMMANDS_DIR)topic.o\ $(COMMANDS_DIR)voice.o\ $(COMMANDS_DIR)wholeft.o\ $(COMMANDS_DIR)znc.o CPPFLAGS += -I $(COMMANDS_DIR) swirc-3.5.5/src/commands/build.w32.mk000066400000000000000000000020451501213070300172760ustar00rootroot00000000000000COMMANDS_DIR = src/commands/ OBJS = $(COMMANDS_DIR)admin.obj\ $(COMMANDS_DIR)announce.obj\ $(COMMANDS_DIR)ban.obj\ $(COMMANDS_DIR)cap.obj\ $(COMMANDS_DIR)cleartoasts.obj\ $(COMMANDS_DIR)colormap.obj\ $(COMMANDS_DIR)connect.obj\ $(COMMANDS_DIR)ctcp.obj\ $(COMMANDS_DIR)dcc-w32.obj\ $(COMMANDS_DIR)dcc.obj\ $(COMMANDS_DIR)echo.obj\ $(COMMANDS_DIR)fetchdic.obj\ $(COMMANDS_DIR)ftp-w32.obj\ $(COMMANDS_DIR)ftp.obj\ $(COMMANDS_DIR)ignore.obj\ $(COMMANDS_DIR)info.obj\ $(COMMANDS_DIR)invite.obj\ $(COMMANDS_DIR)jp.obj\ $(COMMANDS_DIR)kick.obj\ $(COMMANDS_DIR)me.obj\ $(COMMANDS_DIR)misc.obj\ $(COMMANDS_DIR)msg.obj\ $(COMMANDS_DIR)nick.obj\ $(COMMANDS_DIR)notice.obj\ $(COMMANDS_DIR)op.obj\ $(COMMANDS_DIR)sasl-scram-sha.obj\ $(COMMANDS_DIR)sasl.obj\ $(COMMANDS_DIR)say.obj\ $(COMMANDS_DIR)services.obj\ $(COMMANDS_DIR)servlist.obj\ $(COMMANDS_DIR)squery.obj\ $(COMMANDS_DIR)theme.obj\ $(COMMANDS_DIR)topic.obj\ $(COMMANDS_DIR)voice.obj\ $(COMMANDS_DIR)wholeft.obj\ $(COMMANDS_DIR)znc.obj CPPFLAGS = $(CPPFLAGS) -I $(COMMANDS_DIR) swirc-3.5.5/src/commands/cap.cpp000066400000000000000000000046001501213070300165020ustar00rootroot00000000000000/* The cap command Copyright (C) 2022 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../errHand.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "cap.h" /* * usage: /cap [ls | list] */ void cmd_cap(const char *data) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1, true); if (strings_match(data, "")) { if (net_send("CAP LS") < 0 || net_send("CAP LIST") < 0) err_log(ENOTCONN, "/cap"); } else if (strings_match(data, "ls") || strings_match(data, "LS")) { printtext(&ctx, "Capabilities supported by the server:"); (void) net_send("CAP LS 302"); } else if (strings_match(data, "list") || strings_match(data, "LIST")) { printtext(&ctx, "Capabilities associated with " "the active connection:"); (void) net_send("CAP LIST"); } else { ctx.spec_type = TYPE_SPEC1_FAILURE; printtext(&ctx, "what? ls or list?"); } } swirc-3.5.5/src/commands/cap.h000066400000000000000000000001571501213070300161520ustar00rootroot00000000000000#ifndef CMD_CAP_H #define CMD_CAP_H __SWIRC_BEGIN_DECLS void cmd_cap(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/cleartoasts.cpp000066400000000000000000000010241501213070300202600ustar00rootroot00000000000000#include "common.h" #ifdef WIN32 #include "../DesktopNotificationManagerCompat.hpp" #include "../ToastsAPI.hpp" #endif #include "../errHand.h" #include "../printtext.h" #include "../strHand.h" #include "cleartoasts.h" /* * usage: /cleartoasts */ void cmd_cleartoasts(const char *data) { if (!strings_match(data, "")) { print_and_free("/cleartoasts: implicit trailing data", NULL); return; } #if defined(UNIX) debug("cmd_cleartoasts() called (currently a no-op)"); #elif defined(WIN32) Toasts::ClearToasts(); #endif } swirc-3.5.5/src/commands/cleartoasts.h000066400000000000000000000002071501213070300177270ustar00rootroot00000000000000#ifndef CMD_CLEARTOASTS_H #define CMD_CLEARTOASTS_H __SWIRC_BEGIN_DECLS void cmd_cleartoasts(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/colormap.cpp000066400000000000000000000135241501213070300175600ustar00rootroot00000000000000/* colormap.cpp -- output colors Copyright (C) 2020-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "../cursesInit.h" #include "../printtext.h" #include "../strHand.h" #include "colormap.h" #include "i18n.h" #if defined(__cplusplus) && __cplusplus >= 201103L #define OUT_PB(str) out.emplace_back(str) #else #define OUT_PB(str) out.push_back(str) #endif void pb_linux_colors(std::vector &); void pb_other_colors(std::vector &); void pb_linux_colors(std::vector &out) { OUT_PB("\0030,16 16 \0030,17 17 \0030,18 18 \0030,19 19 \0030,20 20 " "\0030,21 21 \0030,22 22 \0030,23 23 \0030,24 24 \0030,25 25 " "\0030,26 26 \0030,27 27 "); OUT_PB("\0030,28 28 \0030,29 29 \0030,30 30 \0030,31 31 \0030,32 32 " "\0030,33 33 \0030,34 34 \0030,35 35 \0030,36 36 \0030,37 37 " "\0030,38 38 \0030,39 39 "); OUT_PB("\0030,40 40 \0030,41 41 \0030,42 42 \0030,43 43 \0030,44 44 " "\0030,45 45 \0030,46 46 \0030,47 47 \0030,48 48 \0030,49 49 " "\0030,50 50 \0030,51 51 "); OUT_PB("\0030,52 52 \0030,53 53 \0031,54 54 \0031,55 55 \0031,56 56 " "\0031,57 57 \0031,58 58 \0030,59 59 \0030,60 60 \0030,61 61 " "\0030,62 62 \0030,63 63 "); OUT_PB("\0030,64 64 \0031,65 65 \0031,66 66 \0031,67 67 \0031,68 68 " "\0031,69 69 \0031,70 70 \0031,71 71 \0030,72 72 \0030,73 73 " "\0030,74 74 \0030,75 75 "); OUT_PB("\0031,76 76 \0031,77 77 \0031,78 78 \0031,79 79 \0031,80 80 " "\0031,81 81 \0031,82 82 \0031,83 83 \0031,84 84 \0031,85 85 " "\0031,86 86 \0031,87 87 "); OUT_PB("\0030,88 88 \0030,89 89 \0030,90 90 \0030,91 91 \0030,92 92 " "\0030,93 93 \0030,94 94 \0030,95 95 \0031,96 96 \0031,97 97 " "\0031,98 98 \00399,99 99 "); } void pb_other_colors(std::vector &out) { OUT_PB("\00316 16 \00317 17 \00318 18 \00319 19 \00320 20 " "\00321 21 \00322 22 \00323 23 \00324 24 \00325 25 " "\00326 26 \00327 27 "); OUT_PB("\00328 28 \00329 29 \00330 30 \00331 31 \00332 32 " "\00333 33 \00334 34 \00335 35 \00336 36 \00337 37 " "\00338 38 \00339 39 "); OUT_PB("\00340 40 \00341 41 \00342 42 \00343 43 \00344 44 " "\00345 45 \00346 46 \00347 47 \00348 48 \00349 49 " "\00350 50 \00351 51 "); OUT_PB("\00352 52 \00353 53 \00354 54 \00355 55 \00356 56 " "\00357 57 \00358 58 \00359 59 \00360 60 \00361 61 " "\00362 62 \00363 63 "); OUT_PB("\00364 64 \00365 65 \00366 66 \00367 67 \00368 68 " "\00369 69 \00370 70 \00371 71 \00372 72 \00373 73 " "\00374 74 \00375 75 "); OUT_PB("\00376 76 \00377 77 \00378 78 \00379 79 \00380 80 " "\00381 81 \00382 82 \00383 83 \00384 84 \00385 85 " "\00386 86 \00387 87 "); OUT_PB("\00388 88 \00389 89 \00390 90 \00391 91 \00392 92 " "\00393 93 \00394 94 \00395 95 \00396 96 \00397 97 " "\00398 98 \00399,99 99 "); } static void print_function_key(PPRINTTEXT_CONTEXT ctx, const char *key, const char *desc) { printtext(ctx, " %c%-3s%c = %s", BOLD, key, NORMAL, desc); } /* usage: /colormap */ void cmd_colormap(const char *data) { PRINTTEXT_CONTEXT ctx; std::vector out; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if (!strings_match(data, "")) { printtext(&ctx, "/colormap: implicit trailing data"); return; } OUT_PB(""); OUT_PB(TXT_BOLD "COLORMAP" TXT_BOLD); OUT_PB(""); OUT_PB("\0031,0 00 \0030,1 01 \0030,2 02 \0030,3 03 " "\0031,4 04 \0030,5 05 \0030,6 06 \0031,7 07 "); OUT_PB("\0031,8 08 \0031,9 09 \0030,10 10 \0031,11 11 " "\0030,12 12 \0031,13 13 \0031,14 14 \0031,15 15 "); OUT_PB(""); #if BSD || LINUX || OS_X pb_linux_colors(out); #else pb_other_colors(out); #endif OUT_PB(""); ctx.spec_type = TYPE_SPEC1; for (const std::string &str : out) printtext(&ctx, "%s", str.c_str()); print_function_key(&ctx, "F5", "Blink"); print_function_key(&ctx, "F6", "Bold"); print_function_key(&ctx, "F7", "Color"); print_function_key(&ctx, "F8", "Normal"); print_function_key(&ctx, "F9", "Reverse"); print_function_key(&ctx, "F10", "Underline"); printtext(&ctx, " "); printtext(&ctx, "COLORS: %d", COLORS); printtext(&ctx, "COLOR_PAIRS: %d", COLOR_PAIRS); printtext(&ctx, "can_change_color: %s", (can_change_color() ? _("Yes") : _("No"))); printtext(&ctx, "g_initialized_pairs: %hd", g_initialized_pairs); } swirc-3.5.5/src/commands/colormap.h000066400000000000000000000001761501213070300172240ustar00rootroot00000000000000#ifndef CMD_COLORMAP_H #define CMD_COLORMAP_H __SWIRC_BEGIN_DECLS void cmd_colormap(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/connect.c000066400000000000000000000475431501213070300170450ustar00rootroot00000000000000/* Connect and Disconnect commands Copyright (C) 2016-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include /* OPENSSL_cleanse() */ #include "../irc.h" #include "../events/welcome.h" #include "../assertAPI.h" #include "../config.h" #include "../curses-funcs.h" #include "../dataClassify.h" #include "../errHand.h" #include "../io-loop.h" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../strdup_printf.h" #include "../terminal.h" #include "connect.h" #include "dcc.h" #include "i18n.h" #if defined(WIN32) && defined(printf) #undef printf #endif volatile bool g_disconnect_wanted = false; static bool quit_reconnecting = false; static bool reconnecting = false; static bool secure_connection = false; static IRC_SERVER afternet_servers[] = { { "irc.afternet.org", "6667", "Default PLAIN" }, { "irc.afternet.org", "6697", "Default TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER alphachat_servers[] = { { "irc.alphachat.net", "6667", "Default PLAIN" }, { "irc.alphachat.net", "6697", "Default TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER anonops_servers[] = { { "irc.anonops.com", "6697", "Main server" }, { NULL, NULL, NULL }, }; static IRC_SERVER efnet_servers[] = { { "efnet.port80.se", "6697", "TLS" }, { "irc.efnet.nl", "6697", "TLS" }, { "irc.efnet.org", "6667", "PLAIN" }, { "irc.mzima.net", "6697", "TLS" }, { "irc.underworld.no", "6697", "TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER freenode_servers[] = { { "chat.freenode.net", "6667", "Default PLAIN" }, { "chat.freenode.net", "6697", "Default TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER ircnet_servers[] = { { "open.ircnet.io", "6667", "All open servers" }, { "ssl.ircnet.ovh", "6697", "SSL/TLS support" }, { "irc.cloak.ircnet.ovh", "6667", "Cloak support" }, { "ssl.cloak.ircnet.ovh", "6697", "Cloak and SSL/TLS support" }, { "eu.irc6.net", "6667", "IPv6 PLAIN" }, { "irc.cs.hut.fi", "6667", "Helsinki Univ of Tech, CS Lab IRC server" }, { "irc.psychz.net", "6667", "US Open Server, Provided by Psychz Networks" }, { "irc.swipnet.se", "8080", "Tele2/SWIPNET" }, { NULL, NULL, NULL }, }; static IRC_SERVER ircnow_servers[] = { { "irc.bsdforall.org", "6697", "IPv4 TLS" }, { "irc.ircforever.org", "6697", "IPv4 TLS" }, { "irc.ircnow.org", "6667", "IPv4 PLAIN" }, { "irc.ircnow.org", "6697", "IPv4 TLS" }, { "irc.nastycode.com", "6697", "IPv4 TLS" }, { "irc.rpblc.net", "6667", "RePuBLiC plain" }, { "irc.rpblc.net", "6697", "RePuBLiC tls" }, { "irc.shelltalk.net", "6697", "IPv4 TLS" }, { "irc.thunderirc.net", "6697", "IPv4 TLS" }, { "irc6.bsdforall.org", "6697", "IPv6 TLS" }, { "irc6.ircforever.org", "6697", "IPv6 TLS" }, { "irc6.ircnow.org", "6667", "IPv6 PLAIN" }, { "irc6.ircnow.org", "6697", "IPv6 TLS" }, { "irc6.nastycode.com", "6697", "IPv6 TLS" }, { "irc6.shelltalk.net", "6697", "IPv6 TLS" }, { "irc6.thunderirc.net", "6697", "IPv6 TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER libera_servers[] = { { "irc.libera.chat", "6667", "Default PLAIN" }, { "irc.libera.chat", "6697", "Default TLS" }, { "irc.eu.libera.chat", "6697", "Europe" }, { "irc.us.libera.chat", "6697", "US & Canada" }, { "irc.au.libera.chat", "6697", "Australia and New Zealand" }, { "irc.ea.libera.chat", "6697", "East Asia"}, { "irc.ipv4.libera.chat", "8000", "IPv4 PLAIN" }, { "irc.ipv4.libera.chat", "6697", "IPv4 TLS" }, { "irc.ipv6.libera.chat", "8000", "IPv6 PLAIN" }, { "irc.ipv6.libera.chat", "6697", "IPv6 TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER oftc_servers[] = { { "irc.oftc.net", "6667", "Default PLAIN" }, { "irc.oftc.net", "6697", "Default TLS" }, { NULL, NULL, NULL }, }; static IRC_SERVER quakenet_servers[] = { { "adrift.sg.quakenet.org", "6667", "Adrift" }, { "datapacket.hk.quakenet.org", "6667", "DataPacket" }, { "euroserv.fr.quakenet.org", "6667", "Euroserv" }, { "hostsailor.ro.quakenet.org", "6667", "HostSailor" }, { "irc.ipv6.quakenet.org", "6667", "IPv6 PLAIN" }, { "port80c.se.quakenet.org", "6667", "" }, { "stockholm.se.quakenet.org", "6667", "Stockholm" }, { "underworld2.no.quakenet.org", "6667", "Underworld" }, { NULL, NULL, NULL }, }; static IRC_SERVER undernet_servers[] = { { "irc.undernet.org", "6667", "IPv4 WorldWide" }, { "irc6.undernet.org", "6667", "IPv6 WorldWide" }, { NULL, NULL, NULL }, }; static IRC_SERVER test_servers[] = { { "default.icb.net", "7326", "Chime ICB server" }, { "internetcitizens.band", "7326", "Fuchsschwanz ICB server PLAIN" }, { "internetcitizens.band", "7327", "Fuchsschwanz ICB server TLS" }, { "testnet.ergo.chat", "6667", "Testnet for the Ergo IRC server PLAIN" }, { "testnet.ergo.chat", "6697", "Testnet for the Ergo IRC server TLS" }, { "testnet.inspircd.org", "6667", "InspIRCd Test Network PLAIN" }, { "testnet.inspircd.org", "6697", "InspIRCd Test Network TLS" }, { NULL, NULL, NULL }, }; static stringarray_t connect_cmds = { "afternet", "alphachat", "anonops", "efnet", "freenode", "ircnet", "ircnow", "libera", "oftc", "quakenet", "undernet", "-tls ", "-tls afternet", "-tls alphachat", "-tls anonops", "-tls efnet", "-tls freenode", "-tls ircnet", "-tls ircnow", "-tls libera", "-tls oftc", "-tls quakenet", "-tls undernet", }; static bool shouldConnectUsingPassword(void) { char answer[20] = { '\0' }; while (true) { fputs(_("Connect using password? [Y/n]: "), stdout); fflush(stdout); if (fgets(answer, sizeof answer, stdin) == NULL) { err_sys("%s: fgets", __func__); } else if (strchr(answer, '\n') == NULL) { int c; puts(_("input too big")); while (c = getchar(), c != '\n' && c != EOF) /* discard */; } else if (strings_match(trim(answer), "") || strings_match(answer, "y") || strings_match(answer, "Y")) { break; } else if (strings_match(answer, "n") || strings_match(answer, "N")) { return false; } else { ; } } return true; } /*lint -sem(get_password, r_null) */ static char * get_password(void) { static char pass[400] = { '\0' }; if (g_cmdline_opts->passwd) { if ((errno = sw_strcpy(pass, g_cmdline_opts->passwd, sizeof pass)) == 0) return (&pass[0]); else { err_log(errno, _("%s: string copy error"), __func__); return NULL; } } escape_curses(); if (!shouldConnectUsingPassword()) { resume_curses(); return NULL; } while (true) { bool fgets_error; int errno_save; fputs(_("Server password (will not echo): "), stdout); fflush(stdout); term_toggle_echo(OFF); errno = 0; fgets_error = (fgets(pass, sizeof pass, stdin) == NULL); errno_save = errno; term_toggle_echo(ON); putchar('\n'); if (fgets_error) { err_exit(errno_save, "%s: fgets", __func__); } else if (strchr(pass, '\n') == NULL) { int c; puts(_("input too big")); while (c = getchar(), c != '\n' && c != EOF) /* discard */; } else if (strings_match(trim(pass), "")) { ; } else { break; } } resume_curses(); return (&pass[0]); } static PIRC_SERVER get_server_v2(PIRC_SERVER ptr, const size_t size, const char *hdr) { PIRC_SERVER srv; STRING msg; char ans[20] = { '\0' }; int i, srvno; escape_curses(); puts(hdr); srv = ptr; i = 0; while (srv->host != NULL) { printf(" (%d) %-30s %s %s\n", i, srv->host, srv->port, srv->desc); srv++; i++; } if (i > 0) i -= 1; srvno = 0; while (true) { msg = strdup_printf(_("Your choice (0-%d): "), i); fputs(msg, stdout); fflush(stdout); free(msg); if (fgets(ans, sizeof ans, stdin) == NULL) { err_sys("%s: fgets", __func__); } else if (strchr(ans, '\n') == NULL) { int c; puts(_("input too big")); while (c = getchar(), c != '\n' && c != EOF) /* discard */; } else if (xsscanf(ans, "%d", &srvno) != 1 || srvno < 0 || srvno > i) { ; } else { break; } } /* while */ UNUSED_PARAM(size); resume_curses(); return ptr + srvno; } static void reconnect_begin(void) { quit_reconnecting = false; reconnecting = true; } static void reconnect_end(void) { reconnecting = false; } static void turn_icb_mode_on(void) { g_icb_mode = true; } static void turn_icb_mode_off(void) { g_icb_mode = false; } static bool assign_username(char **cp) { static char *username; if (g_cmdline_opts->username) { *cp = g_cmdline_opts->username; return true; } else if ((username = Config_mod("username")) != NULL && !strings_match(username, "")) { *cp = username; return true; } else if ((username = g_user) != NULL && !strings_match(username, "")) { *cp = username; return true; } return false; } static bool assign_rl_name(char **cp) { static char *rl_name; if (g_cmdline_opts->rl_name) { *cp = g_cmdline_opts->rl_name; return true; } else if ((rl_name = Config_mod("real_name")) != NULL && !strings_match(rl_name, "")) { *cp = rl_name; return true; } else if ((rl_name = g_user) != NULL && !strings_match(rl_name, "")) { *cp = rl_name; return true; } return false; } static bool assign_nickname(char **cp) { static char *nickname; if (g_cmdline_opts->nickname) { *cp = g_cmdline_opts->nickname; return true; } else if ((nickname = Config_mod("nickname")) != NULL && !strings_match(nickname, "")) { *cp = nickname; return true; } else if ((nickname = g_user) != NULL && !strings_match(nickname, "")) { *cp = nickname; return true; } return false; } PTEXTBUF get_list_of_matching_connect_cmds(const char *search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(connect_cmds); i++) { const char *cmd = connect_cmds[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } void do_connect(const char *server, const char *port, const char *pass) { PRINTTEXT_CONTEXT ptext_ctx; struct network_connect_context conn_ctx = { .server = ((char *) server), .port = ((char *) port), .password = ((char *) pass), .username = "", .rl_name = "", .nickname = "", }; if (atomic_load_bool(&g_connection_in_progress) || g_disconnect_wanted || !g_io_loop || atomic_load_bool(&g_on_air)) return; printtext_context_init(&ptext_ctx, g_status_window, TYPE_SPEC1_FAILURE, true); if (!assign_username(&conn_ctx.username)) { printtext(&ptext_ctx, "Unable to connect: No username"); return; } else if (!assign_rl_name(&conn_ctx.rl_name)) { printtext(&ptext_ctx, "Unable to connect: No real name"); return; } else if (!assign_nickname(&conn_ctx.nickname)) { printtext(&ptext_ctx, "Unable to connect: No nickname"); return; } if (!is_valid_username(conn_ctx.username)) { printtext(&ptext_ctx, "Unable to connect: Invalid username: " "\"%s\"", conn_ctx.username); printtext(&ptext_ctx, "Change with /set username"); return; } else if (!is_valid_real_name(conn_ctx.rl_name)) { printtext(&ptext_ctx, "Unable to connect: Invalid real name: " "\"%s\"", conn_ctx.rl_name); printtext(&ptext_ctx, "Change with /set real_name"); return; } else if (!is_valid_nickname(conn_ctx.nickname)) { printtext(&ptext_ctx, "Unable to connect: Invalid nickname: " "\"%s\"", conn_ctx.nickname); printtext(&ptext_ctx, "Change with /set nickname"); return; } else { /* The value of sleep_time_seconds assigned below * isn't intent to be used. net_connect() is * responsible for giving it an initial value as well * as changing/updating it between reconnect attempts * to reflect the delay. */ long int sleep_time_seconds = 10; if (strings_match(conn_ctx.port, ICB_PORT) || strings_match(conn_ctx.port, ICB_SSL_PORT)) turn_icb_mode_on(); else if (strings_match(conn_ctx.port, IRC_PORT) || strings_match(conn_ctx.port, IRC_SSL_PORT)) turn_icb_mode_off(); if (strings_match(conn_ctx.port, ICB_SSL_PORT) || strings_match(conn_ctx.port, IRC_SSL_PORT) || g_force_tls) set_ssl_on(); reconnect_begin(); ptext_ctx.spec_type = TYPE_SPEC2; while (net_connect(&conn_ctx, &sleep_time_seconds) == SHOULD_RETRY_TO_CONNECT) { printtext(&ptext_ctx, _("Next reconnect attempt in %ld " "seconds..."), sleep_time_seconds); for (long int secs = 0; secs < sleep_time_seconds; secs++) { (void) napms(1000); if (quit_reconnecting) { net_kill_connection(); net_connect_clean_up(); printtext(&ptext_ctx, "%s", _("Reconnection aborted!")); goto out_of_both_loops; } } } out_of_both_loops: reconnect_end(); } } void set_ssl_on(void) { secure_connection = true; } void set_ssl_off(void) { secure_connection = false; } bool ssl_is_enabled(void) { return secure_connection; } #ifdef WIN32 void winsock_init_doit(void) { static bool init_done = false; if (init_done) return; if (!winsock_init()) { printtext_print("err", "%s", _("Cannot initiate use of the " "Winsock DLL")); } else { #if 0 printtext_print("success", "%s", _("Use of the Winsock DLL " "granted")); #endif init_done = true; } } #endif // WIN32 static void choose_server(const char *server, const char *port) { static PIRC_SERVER srvptr; (void) atomic_swap_bool(&g_disconnect_wanted, false); (void) atomic_swap_bool(&g_connection_lost, false); (void) atomic_swap_bool(&g_on_air, false); #ifdef WIN32 winsock_init_doit(); #endif if (strings_match_ignore_case(server, "afternet")) { srvptr = get_server_v2(&afternet_servers[0], ARRAY_SIZE(afternet_servers), "AfterNET IRC network - www.afternet.org"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "alphachat")) { srvptr = get_server_v2(&alphachat_servers[0], ARRAY_SIZE(alphachat_servers), "AlphaChat - www.alphachat.net"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "anonops")) { srvptr = get_server_v2(&anonops_servers[0], ARRAY_SIZE(anonops_servers), "AnonOps IRC network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "efnet")) { srvptr = get_server_v2(&efnet_servers[0], ARRAY_SIZE(efnet_servers), "Eris Free network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "freenode")) { srvptr = get_server_v2(&freenode_servers[0], ARRAY_SIZE(freenode_servers), "Freenode IRC network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "ircnet")) { srvptr = get_server_v2(&ircnet_servers[0], ARRAY_SIZE(ircnet_servers), "IRCnet servers"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "ircnow")) { srvptr = get_server_v2(&ircnow_servers[0], ARRAY_SIZE(ircnow_servers), "IRCNow: The Users' Network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "libera")) { srvptr = get_server_v2(&libera_servers[0], ARRAY_SIZE(libera_servers), "Libera Chat"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "oftc")) { srvptr = get_server_v2(&oftc_servers[0], ARRAY_SIZE(oftc_servers), "The Open and Free Technology Community"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "quakenet")) { srvptr = get_server_v2(&quakenet_servers[0], ARRAY_SIZE(quakenet_servers), "QuakeNet IRC network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "undernet")) { srvptr = get_server_v2(&undernet_servers[0], ARRAY_SIZE(undernet_servers), "Undernet IRC Network"); IRC_CONNECT(srvptr->host, srvptr->port); } else if (strings_match_ignore_case(server, "test")) { srvptr = get_server_v2(&test_servers[0], ARRAY_SIZE(test_servers), "Test servers; both ICB and IRC"); IRC_CONNECT(srvptr->host, srvptr->port); } else { IRC_CONNECT(server, port); } dcc_init(); } /* usage: /connect [-tls] */ void cmd_connect(const char *data) { char *dcopy = sw_strdup(data); char *state = ""; const char *server, *port; int feeds_written = 0; set_ssl_off(); if (strings_match(dcopy, "") || is_whitespace(dcopy)) { print_and_free("/connect: missing arguments", dcopy); return; } else if ((feeds_written = strFeed(dcopy, 1)) == 1) { const char *token; token = strtok_r(dcopy, "\n:", &state); sw_assert(token != NULL); if (strings_match(token, "-tls") || strings_match(token, "-ssl") || g_force_tls) set_ssl_on(); server = strtok_r(NULL, "\n:", &state); sw_assert(server != NULL); if ((port = strtok_r(NULL, "\n:", &state)) == NULL) { if (ssl_is_enabled()) port = IRC_SSL_PORT; else port = IRC_PORT; } } else if (feeds_written == 0) { server = strtok_r(dcopy, "\n:", &state); sw_assert(server != NULL); if ((port = strtok_r(NULL, "\n:", &state)) == NULL) port = IRC_PORT; } else { sw_assert_not_reached(); } if (atomic_load_bool(&g_connection_in_progress)) { print_and_free("/connect: connection in progress", dcopy); return; } else if (reconnecting) { print_and_free("/connect: reconnecting... /disconnect ?", dcopy); return; } else if (atomic_load_bool(&g_on_air)) { print_and_free("/connect: already connected!", dcopy); return; } else if (strtok_r(NULL, "\n:", &state) != NULL) { print_and_free("/connect: implicit trailing data", dcopy); return; } else if (!is_valid_hostname(server)) { print_and_free("/connect: bogus server name", dcopy); return; } else if (!is_numeric(port)) { print_and_free("/connect: bogus port number", dcopy); return; } else { choose_server(server, port); free(dcopy); } } /* usage: /disconnect [message] */ void cmd_disconnect(const char *data) { const bool has_message = !strings_match(data, ""); quit_reconnecting = true; if (atomic_load_bool(&g_on_air)) { net_request_disconnect(); if (has_message) (void) net_send("QUIT :%s", data); else (void) net_send("QUIT :%s", Config("quit_message")); if (atomic_load_bool(&g_connection_in_progress)) event_welcome_signalit(); while (atomic_load_bool(&g_irc_listening)) (void) napms(1); } } swirc-3.5.5/src/commands/connect.h000066400000000000000000000017071501213070300170420ustar00rootroot00000000000000#ifndef CMD_CONNECT_H #define CMD_CONNECT_H #include "../textBuffer.h" #define ICB_PORT "7326" #define ICB_SSL_PORT "7327" #define IRC_PORT "6667" #define IRC_SSL_PORT "6697" #define IRC_CONNECT(m_server, m_port)\ net_do_connect_detached((m_server), (m_port),\ (g_connection_password ? get_password() : NULL)) typedef struct tagIRC_SERVER { const char *host; const char *port; const char *desc; } IRC_SERVER, *PIRC_SERVER; typedef const struct tagIRC_SERVER servarray_const_t[]; typedef const struct tagIRC_SERVER *servarray_const_ptr_t; __SWIRC_BEGIN_DECLS extern volatile bool g_disconnect_wanted; PTEXTBUF get_list_of_matching_connect_cmds(const char *); void do_connect(const char *server, const char *port, const char *pass); void set_ssl_on(void); void set_ssl_off(void); bool ssl_is_enabled(void); #ifdef WIN32 void winsock_init_doit(void); #endif void cmd_connect(const char *); void cmd_disconnect(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/ctcp.c000066400000000000000000000064621501213070300163400ustar00rootroot00000000000000/* ctcp.c Copyright (C) 2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../irc.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "ctcp.h" #include "i18n.h" #include "misc.h" static void query_time(CSTRING p_target) { if (net_send("PRIVMSG %s :\001TIME\001", p_target) > 0) confirm_ctcp_sent("TIME", p_target); } static void query_userinfo(CSTRING p_target) { if (net_send("PRIVMSG %s :\001USERINFO\001", p_target) > 0) confirm_ctcp_sent("USERINFO", p_target); } static void query_version(CSTRING p_target) { if (net_send("PRIVMSG %s :\001VERSION\001", p_target) > 0) confirm_ctcp_sent("VERSION", p_target); } /* * usage: /ctcp */ void cmd_ctcp(CSTRING data) { CSTRING query, target; STRING dcopy; STRING last = ""; static chararray_t cmd = "/ctcp"; static chararray_t sep = "\n"; if (strings_match(data, "")) { printtext_print("err", "%s", _("Insufficient arguments")); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 1); if ((query = strtok_r(dcopy, sep, &last)) == NULL || (target = strtok_r(NULL, sep, &last)) == NULL) { printf_and_free(dcopy, _("%s: insufficient arguments"), cmd); return; } else if (!is_valid_nickname(target) && !is_irc_channel(target)) { printf_and_free(dcopy, _("%s: invalid target"), cmd); return; } else if (is_irc_channel(target) && strpbrk(target, g_forbidden_chan_name_chars) != NULL) { printf_and_free(dcopy, _("%s: forbidden channel name characters"), cmd); return; } if (strings_match(query, "time")) query_time(target); else if (strings_match(query, "userinfo")) query_userinfo(target); else if (strings_match(query, "version")) query_version(target); else printtext_print("err", "%s", _("Invalid CTCP query")); free(dcopy); } swirc-3.5.5/src/commands/ctcp.h000066400000000000000000000002011501213070300163260ustar00rootroot00000000000000#ifndef SRC_COMMANDS_CTCP_H_ #define SRC_COMMANDS_CTCP_H_ __SWIRC_BEGIN_DECLS void cmd_ctcp(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/dcc-unix.cpp000066400000000000000000000047051501213070300174570ustar00rootroot00000000000000/* commands/dcc-unix.cpp Copyright (C) 2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "../assertAPI.h" #include "../errHand.h" #include "dcc.h" static void * dcc_getit(void *arg) { dcc_get *obj = static_cast(arg); obj->get_file(); dcc::exit_thread(); /* NOTREACHED */ return nullptr; } NORETURN void dcc::exit_thread(void) { int dummy = 0; pthread_exit(&dummy); sw_assert_not_reached(); } void dcc::get_file_detached(dcc_get *obj) { pthread_t tid; if ((errno = pthread_create(&tid, nullptr, dcc_getit, obj)) != 0) err_sys("%s: pthread_create", __func__); else if ((errno = pthread_detach(tid)) != 0) err_sys("%s: pthread_detach", __func__); } void dcc::set_recv_timeout(SOCKET sock, const time_t seconds) { struct timeval tv; tv.tv_sec = seconds; tv.tv_usec = 0; errno = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) != 0) err_log(errno, "%s: setsockopt", __func__); } swirc-3.5.5/src/commands/dcc-w32.cpp000066400000000000000000000047241501213070300171100ustar00rootroot00000000000000/* commands/dcc-w32.cpp Copyright (C) 2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "../assertAPI.h" #include "../errHand.h" #include "../tls-server.h" #include "../events/welcome-w32.h" /* dword_product() */ #include "dcc.h" typedef void __cdecl VoidCdecl; static VoidCdecl dcc_getit(void *arg) { dcc_get *obj = static_cast(arg); obj->get_file(); dcc::exit_thread(); } NORETURN void dcc::exit_thread(void) { _endthread(); sw_assert_not_reached(); } void dcc::get_file_detached(dcc_get *obj) { if (_beginthread(dcc_getit, 0, obj) == g_beginthread_failed) err_sys("%s: _beginthread", __func__); } void dcc::set_recv_timeout(SOCKET sock, const DWORD seconds) { const DWORD timeout_milliseconds = dword_product(seconds, 1000); const int optlen = static_cast(sizeof(DWORD)); if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast (&timeout_milliseconds), optlen) != 0) err_log(0, "%s: setsockopt", __func__); } swirc-3.5.5/src/commands/dcc.cpp000066400000000000000000001331221501213070300164720ustar00rootroot00000000000000/* commands/dcc.cpp Copyright (C) 2024-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #if __OpenBSD__ #include #endif #include #include #if UNIX #include #include #include #endif #if HAVE_STD_FS #include #include namespace fs = std::filesystem; #endif #include #if WIN32 #include #endif #include #include #if UNIX #include #endif #include #include #include "../assertAPI.h" #include "../config.h" #include "../dataClassify.h" #include "../errHand.h" #include "../filePred.h" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../network.h" #include "../printtext.h" #include "../sig.h" #include "../strHand.h" #include "../strdup_printf.h" #include "../theme.h" #include "../tls-server.h" #include "dcc.h" #include "i18n.h" #include "theme.h" /* url_to_file() */ #define DCC_FILE_MAX_SIZE (g_one_gig * static_cast(10)) #define DCC_FILE_REQ_SIZE 130 #define DCC_IO_BYTES 2048 #if WIN32 #define stat _stat #endif /**************************************************************** * * * ------------------ Structure definitions ------------------ * * * ****************************************************************/ dcc_get::dcc_get() : filesize(0) , bytes_rem(0) , size(0.0) , unit('B') , start(g_time_error) , stop(g_time_error) , fileptr(nullptr) , sock(INVALID_SOCKET) , ssl(nullptr) , ssl_ctx(nullptr) , addr(0) , port(0) , lock(0) { this->nick.assign(""); this->filename.assign(""); } dcc_get::dcc_get(const char *p_nick, const char *p_filename, intmax_t p_filesize, uint32_t p_addr, uint16_t p_port) : filesize(p_filesize) , bytes_rem(p_filesize) , size(0.0) , unit('B') , start(g_time_error) , stop(g_time_error) , fileptr(nullptr) , sock(INVALID_SOCKET) , ssl(nullptr) , ssl_ctx(nullptr) , addr(p_addr) , port(htons(p_port)) , lock(0) { this->nick.assign(p_nick); this->filename.assign(p_filename); dcc::get_file_size(p_filesize, this->size, this->unit); } dcc_get::dcc_get(const dcc_get &obj) : nick(obj.nick) , filename(obj.filename) , filesize(obj.filesize) , bytes_rem(obj.bytes_rem) , size(obj.size) , unit(obj.unit) , start(g_time_error) , stop(g_time_error) , fileptr(nullptr) , sock(INVALID_SOCKET) , ssl(nullptr) , ssl_ctx(nullptr) , addr(obj.addr) , port(obj.port) , lock(obj.lock) { debug("%s: copy constructor called", __func__); } dcc_get &dcc_get::operator=(const dcc_get &obj) { if (&obj == this) return *this; this->nick = obj.nick; this->filename = obj.filename; this->filesize = obj.filesize; this->bytes_rem = obj.bytes_rem; this->size = obj.size; this->unit = obj.unit; this->start = obj.start; this->stop = obj.stop; this->fileptr = nullptr; this->sock = INVALID_SOCKET; this->ssl = nullptr; this->ssl_ctx = nullptr; this->addr = obj.addr; this->port = obj.port; this->lock = obj.lock; debug("%s: copy assignment called", __func__); return *this; } dcc_get::~dcc_get() { debug("%s: destructor called", __func__); try { this->destroy(); } catch (...) { /* null */; } } static void read_and_write(SOCKET sock, SSL *ssl, FILE *fp, intmax_t &bytes_rem) { dcc::set_recv_timeout(sock, 1); while (bytes_rem > 0 && ssl != nullptr && !(SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)) { char buf[DCC_IO_BYTES] = { '\0' }; int ret; static const int bufsize = static_cast(sizeof buf); ERR_clear_error(); if (!isValid(ssl)) throw std::runtime_error("ssl object invalid"); if ((ret = SSL_read(ssl, addrof(buf[0]), (bytes_rem < bufsize ? static_cast(bytes_rem) : bufsize))) > 0) { if (!isValid(fp) || fwrite(addrof(buf[0]), 1, ret, fp) != static_cast(ret)) throw std::runtime_error(_("Write error")); (void) fflush(fp); bytes_rem -= ret; } else { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_NONE: sw_assert_not_reached(); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: debug("%s: want read / want write", __func__); break; default: const unsigned long int err = ERR_peek_last_error(); throw std::runtime_error(ERR_error_string(err, nullptr)); } } } } void dcc_get::destroy(void) { dcc::shutdown_conn(this->ssl); if (this->sock != INVALID_SOCKET) { #if defined(UNIX) if (shutdown(this->sock, SHUT_RDWR) == -1) err_log(errno, "%s: shutdown", __func__); (void) close(this->sock); #elif defined(WIN32) if (shutdown(this->sock, SD_BOTH) != 0) err_log(errno, "%s: shutdown", __func__); (void) closesocket(this->sock); #endif this->sock = INVALID_SOCKET; } if (this->ssl != nullptr) { SSL_free(this->ssl); this->ssl = nullptr; } if (this->ssl_ctx != nullptr) { SSL_CTX_free(this->ssl_ctx); this->ssl_ctx = nullptr; } } void dcc_get::finalize_download(void) { dcc::shutdown_conn(this->ssl); } void dcc_get::get_file(void) { if (!this->create_socket()) { printtext_print("err", "%s: Error creating the socket", __func__); return; } else if (!this->create_ssl_ctx()) { printtext_print("err", "%s: Error creating the SSL context", __func__); return; } else if (!this->create_ssl_obj()) { printtext_print("err", "%s: Error creating the SSL object", __func__); return; } try { static const int VALUE_HANDSHAKE_OK = 1; struct sockaddr_in sin; // XXX this->set_lock(1); memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = this->port; sin.sin_addr.s_addr = this->addr; if (connect(this->sock, reinterpret_cast (&sin), sizeof sin) == SOCKET_ERROR) throw std::runtime_error("Cannot connect"); else if (!SSL_set_fd(this->ssl, this->sock)) throw std::runtime_error("Set FD error"); SSL_set_connect_state(this->ssl); if (SSL_connect(this->ssl) != VALUE_HANDSHAKE_OK) throw std::runtime_error("TLS/SSL handshake failed!"); else if (this->request_file() == ERR) throw std::runtime_error("Send error"); else if (g_dcc_download_dir == nullptr) throw std::runtime_error("Null dir"); std::string path(g_dcc_download_dir); (void) path.append(SLASH).append(this->filename); if ((this->fileptr = xfopen(path.c_str(), "ab")) == nullptr) throw std::runtime_error("Open failed"); #if defined(UNIX) if (ftruncate(fileno(this->fileptr), 0) != 0) throw std::runtime_error("Change size error"); #elif defined(WIN32) if ((errno = _chsize_s(fileno(this->fileptr), 0)) != 0) throw std::runtime_error("Change size error"); #endif (void) fseek(this->fileptr, 0L, SEEK_END); this->start = time(nullptr); read_and_write(this->sock, this->ssl, this->fileptr, this->bytes_rem); this->stop = time(nullptr); fclose_and_null(addrof(this->fileptr)); dcc::shutdown_conn(this->ssl); if (this->has_completed()) { printtext_print("success", _("%s: wrote: %s"), __func__, path.c_str()); } else { printtext_print("err", _("%s: did not complete: %s"), __func__, path.c_str()); } } catch (const std::runtime_error &e) { fclose_and_null(addrof(this->fileptr)); dcc::shutdown_conn(this->ssl); printtext_print("err", "%s: %s", __func__, e.what()); } } bool dcc_get::has_completed(void) const { if (!(this->filesize > 0)) return false; return (this->bytes_rem == 0); } bool dcc_get::is_locked(void) const { return (this->lock != 0); } void dcc_get::set_lock(int value) { this->lock = value; } bool dcc_get::create_socket(void) { if (this->sock != INVALID_SOCKET) return true; else if ((this->sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) return false; return true; } static int verify_callback(int ok, X509_STORE_CTX *ctx) { if (!ok) { PRINTTEXT_CONTEXT ptext_ctx; X509 *cert = X509_STORE_CTX_get_current_cert(ctx); char issuer[256] = { '\0' }; char subject[256] = { '\0' }; const int depth = X509_STORE_CTX_get_error_depth(ctx); const int err = X509_STORE_CTX_get_error(ctx); if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) return 1; (void) X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof issuer); (void) X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof subject); printtext_context_init(&ptext_ctx, g_status_window, TYPE_SPEC1_WARN, true); printtext(&ptext_ctx, "Error with certificate at depth: %d", depth); printtext(&ptext_ctx, " issuer = %s", issuer); printtext(&ptext_ctx, " subject = %s", subject); printtext(&ptext_ctx, "Reason: %s", X509_verify_cert_error_string(err)); } return ok; } static void set_ciphers_doit(SSL_CTX *ctx, const char *list, bool &ok) { if (!SSL_CTX_set_cipher_list(ctx, list)) ok = false; } static void set_ciphers(SSL_CTX *ctx) { bool assignment_ok = true; const char *cs = Config("dcc_cipher_suite"); if (strings_match(cs, "secure") || strings_match(cs, "SECURE")) set_ciphers_doit(ctx, g_suite_secure, assignment_ok); else if (strings_match(cs, "compat") || strings_match(cs, "COMPAT")) set_ciphers_doit(ctx, g_suite_compat, assignment_ok); else if (strings_match(cs, "legacy") || strings_match(cs, "LEGACY")) set_ciphers_doit(ctx, g_suite_legacy, assignment_ok); else if (strings_match(cs, "all") || strings_match(cs, "ALL")) set_ciphers_doit(ctx, g_suite_all, assignment_ok); else set_ciphers_doit(ctx, g_suite_compat, assignment_ok); if (!assignment_ok) throw std::runtime_error("no valid ciphers"); } bool dcc_get::create_ssl_ctx(void) { if (this->ssl_ctx != nullptr) return true; try { if ((this->ssl_ctx = SSL_CTX_new(TLS_client_method())) == nullptr) throw std::runtime_error("out of memory"); std::string ca_file(g_home_dir); std::string certfile(g_home_dir); (void) ca_file.append(SLASH).append(ROOT_PEM); (void) certfile.append(SLASH).append(CLIENT_PEM); if (!SSL_CTX_load_verify_locations(this->ssl_ctx, ca_file.c_str(), nullptr)) { throw std::runtime_error("load verify locations error"); } else if (!SSL_CTX_set_default_verify_paths(this->ssl_ctx)) { throw std::runtime_error("set default verify paths " "error"); } else if (SSL_CTX_use_certificate_chain_file(this->ssl_ctx, certfile.c_str()) != 1) { throw std::runtime_error("use certificate chain file " "error"); } else if (SSL_CTX_use_PrivateKey_file(this->ssl_ctx, certfile.c_str(), SSL_FILETYPE_PEM) != 1) { throw std::runtime_error("use private key file error"); } SSL_CTX_set_verify(this->ssl_ctx, SSL_VERIFY_PEER, verify_callback); SSL_CTX_set_verify_depth(this->ssl_ctx, 4); if (!SSL_CTX_set_min_proto_version(this->ssl_ctx, TLS1_2_VERSION)) { throw std::runtime_error("error setting minimum " "supported protocol version"); } set_ciphers(this->ssl_ctx); } catch (const std::runtime_error &e) { if (this->ssl_ctx != nullptr) { SSL_CTX_free(this->ssl_ctx); this->ssl_ctx = nullptr; } printtext_print("warn", "%s: %s", __func__, e.what()); return false; } return true; } bool dcc_get::create_ssl_obj(void) { if (this->ssl != nullptr) return true; else if (this->ssl_ctx == nullptr) return false; else if ((this->ssl = SSL_new(this->ssl_ctx)) == nullptr) return false; return true; } int dcc_get::request_file(void) { char buf[DCC_FILE_REQ_SIZE]; const char *bufptr; int buflen; memset(buf, 0, sizeof buf); if (sw_strcpy(buf, g_my_nickname, sizeof buf) != 0 || sw_strcat(buf, "\r\n", sizeof buf) != 0 || sw_strcat(buf, this->filename.c_str(), sizeof buf) != 0) return ERR; bufptr = addrof(buf[0]); buflen = sizeof buf; while (buflen > 0) { int ret; if (this->ssl == nullptr || (SSL_get_shutdown(this->ssl) & SSL_RECEIVED_SHUTDOWN)) return ERR; ERR_clear_error(); if ((ret = SSL_write(this->ssl, bufptr, buflen)) > 0) { if (BIO_flush(SSL_get_wbio(this->ssl)) != 1) debug("%s: error flushing write bio", __func__); bufptr += ret; buflen -= ret; } else { switch (SSL_get_error(this->ssl, ret)) { case SSL_ERROR_NONE: sw_assert_not_reached(); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: debug("%s: want read / want write", __func__); continue; } return ERR; } } return OK; } class dcc_send { public: std::string nick; std::string full_path; FILE *fileptr; intmax_t bytes_rem; double size; char unit; time_t start; time_t stop; dcc_send(); dcc_send(const char *, const std::string); ~dcc_send(); const char *get_filename(void); intmax_t get_filesize(void) const; bool has_completed(void) const; bool is_locked(void) const; void set_lock(int); private: char buf[255]; int lock; struct stat sb; }; dcc_send::dcc_send() : fileptr(nullptr) , bytes_rem(0) , size(0.0) , unit('B') , start(g_time_error) , stop(g_time_error) , lock(0) { this->nick.assign(""); this->full_path.assign(""); BZERO(addrof(this->buf[0]), sizeof this->buf); BZERO(addrof(this->sb), sizeof this->sb); } dcc_send::dcc_send(const char *p_nick, const std::string p_full_path) : fileptr(nullptr) , bytes_rem(0) , size(0.0) , unit('B') , start(g_time_error) , stop(g_time_error) , lock(0) { this->nick.assign(p_nick); this->full_path.assign(p_full_path); BZERO(addrof(this->buf[0]), sizeof this->buf); BZERO(addrof(this->sb), sizeof this->sb); errno = 0; if (stat(p_full_path.c_str(), addrof(this->sb)) != 0) { char strerrbuf[MAXERROR] = { '\0' }; throw std::runtime_error(xstrerror(errno, strerrbuf, sizeof strerrbuf)); } this->bytes_rem = this->get_filesize(); dcc::get_file_size(this->get_filesize(), this->size, this->unit); } dcc_send::~dcc_send() { fclose_and_null(addrof(this->fileptr)); } const char * dcc_send::get_filename(void) { char *full_path_copy; const char *cp; if (!strings_match(this->buf, "")) return addrof(this->buf[0]); if (this->full_path.empty()) return ""; full_path_copy = sw_strdup(this->full_path.c_str()); if ((cp = strrchr(full_path_copy, PATH_SEP)) == nullptr || sw_strcpy(this->buf, cp + 1, sizeof this->buf) != 0) { free(full_path_copy); return ""; } free(full_path_copy); return addrof(this->buf[0]); } intmax_t dcc_send::get_filesize(void) const { return static_cast(this->sb.st_size); } bool dcc_send::has_completed(void) const { return (this->bytes_rem == 0); } bool dcc_send::is_locked(void) const { return (this->lock != 0); } void dcc_send::set_lock(int value) { this->lock = value; } #if HAVE_STD_FS class disk_file { public: std::string name; disk_file(); disk_file(const char *, FileType, intmax_t, fs::perms); FileType get_type(void) const { return (this->type); } intmax_t get_size(void) const { return (this->size); } fs::perms get_perms(void) const { return (this->perms); } private: FileType type; intmax_t size; fs::perms perms; }; disk_file::disk_file() : type(TYPE_unknown) , size(0) , perms(fs::perms::none) { this->name.assign(""); } disk_file::disk_file(const char *p_name, FileType p_type, intmax_t p_size, fs::perms p_perms) : type(p_type) , size(p_size) , perms(p_perms) { this->name.assign(p_name); } #endif // HAVE_STD_FS /**************************************************************** * * * -------------- Objects with external linkage -------------- * * * ****************************************************************/ const int g_one_kilo = 1000; const int g_one_meg = 1000000; const int g_one_gig = 1000000000; /**************************************************************** * * * -------------- Objects with internal linkage -------------- * * * ****************************************************************/ static const std::vector::size_type GET_DB_MAX = 100; static const std::vector::size_type SEND_DB_MAX = 100; static stringarray_t dcc_cmds = { "clear ", "clear get", "clear send", "clear completed", "clear all", "get ", "list ", "list get", "list send", "list all", "ls ", "ls up", "ls down", "send ", }; static std::vector get_db; static std::vector send_db; /**************************************************************** * * * --------------------- Functions --------------------- * * * ****************************************************************/ double percentage(double part, double total) { return (part / total) * 100.0; } static bool find_get_obj(const char *nick, const char *file, std::vector::size_type &pos) { pos = 0; for (const dcc_get &x : get_db) { if (strings_match(x.nick.c_str(), nick) && strings_match(x.filename.c_str(), file)) return true; pos++; } return false; } static bool find_send_obj(const std::string &nick, const char *filename, std::vector::size_type &pos) { pos = 0; for (dcc_send &x : send_db) { if (x.nick.compare(nick) == 0 && strings_match(x.get_filename(), filename)) return true; pos++; } return false; } static void dup_check_get(const char *nick, const char *file) { std::vector::size_type pos = 0; if (!find_get_obj(nick, file, pos)) return; else if (!get_db[pos].has_completed()) throw std::runtime_error("already getting a such nick/file"); else get_db.erase(get_db.begin() + pos); } static void dup_check_send(const char *nick, const char *file) { const std::string str(nick); std::vector::size_type pos = 0; if (!find_send_obj(str, file, pos)) return; else if (!send_db[pos].has_completed()) throw std::runtime_error("already sending a such nick/file"); else send_db.erase(send_db.begin() + pos); } static bool subcmd_ok(const char *subcmd) { if (strings_match(subcmd, "clear")) return true; else if (strings_match(subcmd, "get")) return true; else if (strings_match(subcmd, "list")) return true; else if (strings_match(subcmd, "ls")) return true; else if (strings_match(subcmd, "send")) return true; return false; } static void clear_completed_get_jobs(void) { std::vector::iterator it = get_db.begin(); while (it != get_db.end()) { if (it->has_completed()) it = get_db.erase(it); else ++it; } } static void clear_completed_send_jobs(void) { std::vector::iterator it = send_db.begin(); while (it != send_db.end()) { if (it->has_completed()) it = send_db.erase(it); else ++it; } } static void clear_completed(void) { if (!get_db.empty()) clear_completed_get_jobs(); if (!send_db.empty()) clear_completed_send_jobs(); } static void subcmd_clear(const char *what) { if (what == nullptr || strings_match(what, "")) { printtext_print("err", "insufficient args"); } else if (strings_match(what, "get")) { if (!get_db.empty()) get_db.clear(); printtext_print("success", "cleared the get database"); } else if (strings_match(what, "send")) { if (!send_db.empty()) send_db.clear(); printtext_print("success", "cleared the send database"); } else if (strings_match(what, "completed")) { clear_completed(); printtext_print("success", "cleared completed jobs"); } else if (strings_match(what, "all")) { if (!get_db.empty()) get_db.clear(); if (!send_db.empty()) send_db.clear(); printtext_print("success", "cleared all"); } else { printtext_print("err", "what? get, send, completed or all?"); } } static void subcmd_get(const char *nick, const char *file) { std::vector::size_type pos = 0; if (!is_valid_nickname(nick)) { printtext_print("err", "%s: invalid nickname: %s", __func__, nick); return; } else if (!is_valid_filename(file)) { printtext_print("err", "%s: invalid filename: %s", __func__, file); return; } else if (!find_get_obj(nick, file, pos)) { printtext_print("err", "%s: no such nick/file", __func__); return; } else if (get_db[pos].has_completed()) { printtext_print("err", "%s: has completed", __func__); return; } else if (get_db[pos].start != g_time_error && get_db[pos].stop == g_time_error) { printtext_print("err", "%s: in progress!", __func__); return; } else if (get_db[pos].is_locked()) { printtext_print("err", "%s: get object locked!", __func__); return; } dcc::get_file_detached(addrof(get_db[pos])); } static void get_time(std::string &str, time_t secs) { char buf[200] = {'\0'}; struct tm tm_var = {0}; if (secs == g_time_error) { (void) str.assign("No"); return; } #if defined(UNIX) if (localtime_r(&secs, &tm_var) == nullptr) { (void) str.assign("Not available"); return; } #elif defined(WIN32) if (localtime_s(&tm_var, &secs) != 0) { (void) str.assign("Not available"); return; } #endif if (strftime(buf, ARRAY_SIZE(buf), "%c", &tm_var) > 0) { (void) str.assign(addrof(buf[0])); return; } (void) str.assign("Not available"); } static void list_get(void) { PRINTTEXT_CONTEXT ctx; long int objnum = 0; printtext_context_init(&ctx, g_active_window, TYPE_SPEC2, true); for (dcc_get &x : get_db) { std::string str1(""); std::string str2(""); printtext(&ctx, "----- %sGet object%s: %ld -----", COLOR1, TXT_NORMAL, objnum); printtext(&ctx, "%sFrom%s: %s", COLOR2, TXT_NORMAL, x.nick.c_str()); printtext(&ctx, "%sName%s: %s", COLOR2, TXT_NORMAL, x.filename.c_str()); printtext(&ctx, "%sSize%s: %.1f%c", COLOR2, TXT_NORMAL, x.size, x.unit); printtext(&ctx, "%sHas completed%s: %s (%.2f%%)", COLOR2, TXT_NORMAL, (x.has_completed() ? _("Yes") : _("No")), percentage(TO_DBL(x.filesize - x.bytes_rem), TO_DBL(x.filesize))); get_time(str1, x.start); get_time(str2, x.stop); printtext(&ctx, _("%sStarted%s: %s"), COLOR2, TXT_NORMAL, str1.c_str()); printtext(&ctx, _("%sStopped%s: %s"), COLOR2, TXT_NORMAL, str2.c_str()); objnum++; } ctx.spec_type = TYPE_SPEC1_WARN; if (objnum == 0) printtext(&ctx, "0 get objects"); } static void list_send(void) { PRINTTEXT_CONTEXT ctx; intmax_t filesize; long int objnum = 0; printtext_context_init(&ctx, g_active_window, TYPE_SPEC2, true); for (dcc_send &x : send_db) { filesize = x.get_filesize(); printtext(&ctx, "----- %sSend object%s: %ld -----", COLOR1, TXT_NORMAL, objnum); printtext(&ctx, "%sTo%s: %s", COLOR2, TXT_NORMAL, x.nick.c_str()); printtext(&ctx, "%sName%s: %s", COLOR2, TXT_NORMAL, x.get_filename()); printtext(&ctx, "%sSize%s: %.1f%c", COLOR2, TXT_NORMAL, x.size, x.unit); printtext(&ctx, "%sHas completed%s: %s (%.2f%%)", COLOR2, TXT_NORMAL, (x.has_completed() ? _("Yes") : _("No")), percentage(TO_DBL(filesize - x.bytes_rem), TO_DBL(filesize))); objnum++; } ctx.spec_type = TYPE_SPEC1_WARN; if (objnum == 0) printtext(&ctx, "0 send objects"); } static void subcmd_list(const char *what) { if (what == nullptr || strings_match(what, "")) { printtext_print("err", "insufficient args"); } else if (strings_match(what, "get")) { list_get(); } else if (strings_match(what, "send")) { list_send(); } else if (strings_match(what, "all")) { list_get(); list_send(); } else { printtext_print("err", "what? get, send or all"); } } #if HAVE_STD_FS static FileType get_file_type(const fs::directory_entry &dir_ent) { FileType out = TYPE_unknown; if (dir_ent.is_symlink()) out = TYPE_symlink; else if (dir_ent.is_block_file()) out = TYPE_block_file; else if (dir_ent.is_character_file()) out = TYPE_character_file; else if (dir_ent.is_directory()) out = TYPE_directory; else if (dir_ent.is_fifo()) out = TYPE_fifo; else if (dir_ent.is_regular_file()) out = TYPE_regular_file; else if (dir_ent.is_socket()) out = TYPE_socket; else if (dir_ent.is_other()) out = TYPE_other; else out = TYPE_unknown; return out; } static std::vector get_file_list(const char *dir) { fs::path path = dir; fs::directory_iterator dir_it(path); std::vector df_vec; for (const fs::directory_entry &dir_ent : dir_it) { FileType type; char *name; const char *cp; fs::perms perms; intmax_t size; if (!dir_ent.exists()) continue; name = sw_strdup(dir_ent.path().string().c_str()); if ((cp = strrchr(name, SLASH_CHAR)) == nullptr) cp = name; else cp++; if ((type = get_file_type(dir_ent)) == TYPE_regular_file) size = dir_ent.file_size(); else size = 0; perms = dir_ent.status().permissions(); disk_file file(cp, type, size, perms); #if defined(__cplusplus) && __cplusplus >= 201103L df_vec.emplace_back(file); #else df_vec.push_back(file); #endif free(name); } return (df_vec); } static bool file_list_cmp(const disk_file &obj1, const disk_file &obj2) { const std::string name1(obj1.name); const std::string name2(obj2.name); for (size_t i = 0; i < name1.length() && i < name2.length(); i++) { int c1, c2; c1 = sw_isupper(name1[i]) ? tolower(name1[i]) : name1[i]; c2 = sw_isupper(name2[i]) ? tolower(name2[i]) : name2[i]; if (c1 < c2) return true; else if (c1 > c2) return false; } return (name1.length() < name2.length() ? true : false); } static bool is_exec(fs::perms perms) { #if defined(UNIX) #define PCHK(_perms, _x) (((_perms) & (_x)) != fs::perms::none) return (PCHK(perms, fs::perms::owner_exec) || PCHK(perms, fs::perms::group_exec) || PCHK(perms, fs::perms::others_exec)); #elif defined(WIN32) UNUSED_PARAM(perms); return false; #endif } #endif // HAVE_STD_FS void list_dir(const char *dir) { #if HAVE_STD_FS static const std::string ext1(".EXE"); static const std::string ext2(".exe"); std::vector df_vec; if (!is_directory(dir)) return; try { df_vec = get_file_list(dir); std::sort(df_vec.begin(), df_vec.end(), file_list_cmp); } catch (const std::exception &e) { printtext_print("err", "%s: %s", __func__, e.what()); return; } printtext_print("none", "--- BEGIN: %s ---", dir); for (disk_file &df : df_vec) { char *str = nullptr; switch (df.get_type()) { case TYPE_symlink: str = strdup_printf("%s%s%s%s", COLOR_symlink, df.name.c_str(), TXT_NORMAL, SYM_symlink); break; case TYPE_block_file: case TYPE_character_file: str = strdup_printf("%s%s%s", COLOR_device, df.name.c_str(), TXT_NORMAL); break; case TYPE_directory: str = strdup_printf("%s%s%s%s", COLOR_directory, df.name.c_str(), TXT_NORMAL, SLASH); break; case TYPE_fifo: str = strdup_printf("%s%s%s%s", COLOR_fifo, df.name.c_str(), TXT_NORMAL, SYM_fifo); break; case TYPE_regular_file: if (is_exec(df.get_perms()) || std::equal(ext1.rbegin(), ext1.rend(), df.name.rbegin()) || std::equal(ext2.rbegin(), ext2.rend(), df.name.rbegin())) { str = strdup_printf("%s%s%s%s", COLOR_exec, df.name.c_str(), TXT_NORMAL, SYM_exec); } else { double size = 0.0; char unit = 'B'; dcc::get_file_size(df.get_size(), size, unit); str = strdup_printf("%s %s%.1f%c%s", df.name.c_str(), LEFT_BRKT, size, unit, RIGHT_BRKT); } break; case TYPE_socket: str = strdup_printf("%s%s%s%s", COLOR_socket, df.name.c_str(), TXT_NORMAL, SYM_socket); break; case TYPE_other: str = strdup_printf("%s %s%s%s", df.name.c_str(), LEFT_BRKT, "other", RIGHT_BRKT); break; case TYPE_unknown: default: str = strdup_printf("%s %s%s%s", df.name.c_str(), LEFT_BRKT, "unknown", RIGHT_BRKT); break; } if (str) { printtext_print("sp2", "%s", str); free(str); } } // for printtext_print("none", "--- END: %s ---", dir); #else #pragma message("No C++ standard filesystem") UNUSED_PARAM(dir); printtext_print("err", "operation not supported"); #endif } PTEXTBUF get_list_of_matching_dcc_cmds(const char *search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(dcc_cmds); i++) { const char *cmd = dcc_cmds[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return nullptr; } return matches; } static void subcmd_ls(const char *what) { if (what == nullptr || strings_match(what, "")) { printtext_print("err", "insufficient args"); } else if (strings_match(what, "up")) { list_dir(dcc::get_upload_dir()); } else if (strings_match(what, "down")) { list_dir(g_dcc_download_dir); } else { printtext_print("err", "what? uploads or downloads?"); } } static void subcmd_send(const char *nick, const char *file) { char *nick_lc = nullptr; struct integer_context intctx("dcc_port", 1024, 65535, 8080); if (nick == nullptr || file == nullptr) { printtext_print("err", "insufficient args"); return; } else if (!is_valid_nickname(nick)) { printtext_print("err", "invalid nickname"); return; } else if (!is_valid_filename(file)) { printtext_print("err", "invalid filename"); return; } else if (g_my_nickname == nullptr) { printtext_print("err", "variable unexpectedly set to null"); return; } else if (strings_match_ignore_case(nick, g_my_nickname)) { printtext_print("err", "cannot send to yourself"); return; } else if (!(send_db.size() < SEND_DB_MAX)) { printtext_print("err", "database full"); return; } std::string full_path(dcc::get_upload_dir()); (void) full_path.append(SLASH).append(file); if (!file_exists(full_path.c_str())) { printtext_print("err", "file doesn't exist"); return; } else if (!is_regular_file(full_path.c_str())) { printtext_print("err", "file isn't a regular file"); return; } try { int ret = -1; std::string ext_ip(""); uint32_t addr = 0; if (!dcc::get_remote_addr(ext_ip, addr)) { throw std::runtime_error("error getting the remote " "address"); } nick_lc = strToLower(sw_strdup(nick)); dup_check_send(nick_lc, file); #if defined(__cplusplus) && __cplusplus >= 201103L dcc_send send_obj(nick_lc, std::move(full_path)); #else dcc_send send_obj(nick_lc, full_path); #endif if (send_obj.get_filesize() > DCC_FILE_MAX_SIZE) throw std::runtime_error("too large file"); send_db.push_back(send_obj); ret = net_send("PRIVMSG %s :%cSW_DCC SEND " "%" PRIu32 " %ld " "%" PRIdMAX " %s%c", nick_lc, g_ascii_soh, addr, config_integer(&intctx), send_obj.get_filesize(), send_obj.get_filename(), g_ascii_soh); if (ret < 0) { send_db.pop_back(); throw std::runtime_error("cannot send"); } printtext_print("sp1", _("sending '%s' to %s (%.1f%c)..."), send_obj.get_filename(), nick, send_obj.size, send_obj.unit); } catch (const std::runtime_error &e) { printtext_print("err", "%s", e.what()); } catch (...) { /* null */; } free(nick_lc); } /* * usage: * /dcc [clear|get|list|ls|send] [args] * /dcc clear [get|send|completed|all] * /dcc get * /dcc list [get|send|all] * /dcc ls [up|down] * /dcc send */ void cmd_dcc(const char *data) { char *dcopy; char *last = const_cast(""); const char *subcmd, *arg1, *arg2; static const char cmd[] = "/dcc"; static const char sep[] = "\n"; if (strings_match(data, "")) { printtext_print("err", "insufficient args"); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 2); if ((subcmd = strtok_r(dcopy, sep, &last)) == nullptr) { printf_and_free(dcopy, "%s: insufficient args", cmd); return; } else if (!subcmd_ok(subcmd)) { printf_and_free(dcopy, "%s: invalid subcommand '%s'", cmd, subcmd); return; } arg1 = strtok_r(nullptr, sep, &last); arg2 = strtok_r(nullptr, sep, &last); if (strings_match(subcmd, "clear")) subcmd_clear(arg1); else if (strings_match(subcmd, "get")) subcmd_get(arg1, arg2); else if (strings_match(subcmd, "list")) subcmd_list(arg1); else if (strings_match(subcmd, "ls")) subcmd_ls(arg1); else if (strings_match(subcmd, "send")) subcmd_send(arg1, arg2); else printtext_print("err", "%s: invalid subcommand", cmd); free(dcopy); } static bool has_all_certs(void) { std::string path[4]; (void) path[0].assign(g_home_dir).append(SLASH).append(ROOT_PEM); (void) path[1].assign(g_home_dir).append(SLASH).append(SERVER_CA_PEM); (void) path[2].assign(g_home_dir).append(SLASH).append(SERVER_PEM); (void) path[3].assign(g_home_dir).append(SLASH).append(CLIENT_PEM); if (!file_exists(path[0].c_str()) || !file_exists(path[1].c_str()) || !file_exists(path[2].c_str()) || !file_exists(path[3].c_str())) return false; return true; } void dcc_init(void) { if (config_bool("dcc", true) && !g_icb_mode) { struct integer_context intctx("dcc_port", 1024, 65535, 8080); if (!has_all_certs()) { printtext_print("warn", "%s", _("Missing certs. " "Not starting the DCC server. " "Please create them.")); printtext_print("warn", "(%s, %s, %s and %s)", ROOT_PEM, SERVER_CA_PEM, SERVER_PEM, CLIENT_PEM); return; } tls_server::begin(config_integer(&intctx)); } } void dcc_deinit(void) { tls_server::end(); if (!get_db.empty()) get_db.clear(); if (!send_db.empty()) send_db.clear(); (void) napms(100); } int imax_to_int(const char *fn, const intmax_t i) { if (i > INT_MAX) { char buf[200] = { '\0' }; (void)snprintf(buf, sizeof buf, "%s: integer maximum exceeded", fn); throw std::overflow_error(&buf[0]); } return static_cast(i); } void dcc::add_file(const char *nick, const char *user, const char *host, const char *data) { char *dcopy; char *last = const_cast(""); char *nick_lc = nullptr; char *token[4] = { nullptr }; static const char sep[] = "\n"; if (!is_valid_nickname(nick) || strings_match(data, "")) return; dcopy = sw_strdup(data); if (strFeed(dcopy, 3) != 3) { free(dcopy); return; } token[0] = strtok_r(dcopy, sep, &last); token[1] = strtok_r(nullptr, sep, &last); token[2] = strtok_r(nullptr, sep, &last); token[3] = strtok_r(nullptr, sep, &last); if (token[0] == nullptr || token[1] == nullptr || token[2] == nullptr || token[3] == nullptr) { free(dcopy); return; } else if (!is_numeric(token[0]) || !is_numeric(token[1]) || !is_numeric(token[2])) { free(dcopy); return; } else if (token[0][0] == '0' || token[1][0] == '0' || token[2][0] == '0') { free(dcopy); return; } else if (!is_valid_filename(token[3])) { free(dcopy); return; } try { uint32_t addr = 0; uint16_t port = 0; intmax_t filesize = 0; if (!(get_db.size() < GET_DB_MAX)) throw std::runtime_error("database full"); else if (xsscanf(token[0], "%" SCNu32, &addr) != 1) throw std::runtime_error("error getting the address"); else if (xsscanf(token[1], "%" SCNu16, &port) != 1) throw std::runtime_error("error getting the port"); else if (xsscanf(token[2], "%" SCNdMAX, &filesize) != 1) throw std::runtime_error("error getting the file size"); else if (filesize > DCC_FILE_MAX_SIZE) throw std::runtime_error("too large file"); nick_lc = strToLower(sw_strdup(nick)); dup_check_get(nick_lc, token[3]); dcc_get get_obj(nick_lc, token[3], filesize, addr, port); get_db.push_back(get_obj); printtext_print("sp3", _("%s: added: '%s' (%.1f%c)"), __func__, token[3], get_obj.size, get_obj.unit); printtext_print("sp3", _("%s: from: %s <%s@%s>"), __func__, nick, user, host); printtext_print("sp2", "%s", _("To get the file, type:")); printtext_print("sp2", " /dcc get %s %s", nick_lc, token[3]); } catch (const std::runtime_error &e) { printtext_print("err", "%s: %s", __func__, e.what()); printtext_print("err", "%s: %s <%s@%s>", __func__, nick, user, host); } free(dcopy); free(nick_lc); } void dcc::get_file_size(const intmax_t bytes, double &size, char &unit) { if (bytes <= 0) { size = 0.0; unit = 'B'; } else if (bytes >= g_one_gig) { size = static_cast(bytes) / g_one_gig; unit = 'G'; } else if (bytes >= g_one_meg) { size = static_cast(bytes) / g_one_meg; unit = 'M'; } else if (bytes >= g_one_kilo) { size = static_cast(bytes) / g_one_kilo; unit = 'K'; } else { size = static_cast(bytes); unit = 'B'; } } bool dcc::get_remote_addr(std::string &str, uint32_t &addr) { const char *own_ip = Config("dcc_own_ip"); if (strings_match(own_ip, "")) { FILE *fileptr = nullptr; char ext_ip[30] = { '\0' }; std::string url(g_swircWebAddr); std::string path(g_tmp_dir); (void) url.append("ext_ip/"); (void) path.append(SLASH).append("ext_ip.tmp"); url_to_file(url.c_str(), path.c_str()); if (!file_exists(path.c_str()) || (fileptr = xfopen(path.c_str(), "r")) == nullptr || fgets(ext_ip, sizeof ext_ip, fileptr) == nullptr || strchr(ext_ip, '\n') == nullptr) { if (fileptr) (void) fclose(fileptr); if (file_exists(path.c_str())) { if (remove(path.c_str()) != 0) err_log(errno, "%s: remove", __func__); } (void) str.assign(""); addr = INADDR_NONE; return false; } ext_ip[strcspn(ext_ip, "\n")] = '\0'; (void) fclose(fileptr); if (remove(path.c_str()) != 0) err_log(errno, "%s: remove", __func__); if ((addr = inet_addr(ext_ip)) == INADDR_NONE) { (void) str.assign(""); return false; } (void) str.assign(ext_ip); return true; } if ((addr = inet_addr(own_ip)) == INADDR_NONE) { (void) str.assign(""); return false; } (void) str.assign(own_ip); return true; } const char * dcc::get_upload_dir(void) { static const char *dir; dir = Config("dcc_upload_dir"); #if defined(OpenBSD) && OpenBSD >= 201811 return (strings_match(dir, "") ? g_dcc_upload_dir : dir); #else if (!is_directory(dir)) return (g_dcc_upload_dir ? g_dcc_upload_dir : ""); return dir; #endif } static int read_request(SSL *ssl, std::string &nick, std::string &filename) { char *bufptr; char *last = const_cast(""); char *token[2] = { nullptr }; char buf[DCC_FILE_REQ_SIZE] = { '\0' }; int buflen; static const char sep[] = "\r\n"; bufptr = addrof(buf[0]); buflen = sizeof buf; do { int ret; if (ssl == nullptr || (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)) return ERR; ERR_clear_error(); if ((ret = SSL_read(ssl, bufptr, buflen)) > 0) { bufptr += ret; buflen -= ret; } else { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_NONE: sw_assert_not_reached(); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: debug("%s: want read / want write", __func__); break; default: return ERR; } } } while (buflen > 0); if (memchr(buf, '\0', sizeof buf) == nullptr || (token[0] = strtok_r(buf, sep, &last)) == nullptr || (token[1] = strtok_r(nullptr, sep, &last)) == nullptr) return ERR; else if (!is_valid_nickname(token[0])) return ERR; else if (!is_valid_filename(token[1])) return ERR; (void) nick.assign(token[0]); (void) filename.assign(token[1]); return OK; } static int send_bytes(SSL *ssl, const char *buf, const int bytes, intmax_t &bytes_rem) { const char *bufptr = buf; int buflen = bytes; while (buflen > 0) { int ret; if (ssl == nullptr || (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)) return ERR; ERR_clear_error(); if ((ret = SSL_write(ssl, bufptr, buflen)) > 0) { if (BIO_flush(SSL_get_wbio(ssl)) != 1) debug("%s: error flushing write bio", __func__); bufptr += ret; buflen -= ret; bytes_rem -= ret; } else { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_NONE: sw_assert_not_reached(); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: debug("%s: want read / want write", __func__); continue; } return ERR; } } return OK; } static int accept_incoming(SSL *ssl) { bool loop = true; while (loop) { const int ret = SSL_accept(ssl); switch (ret) { case 0: debug("%s: SSL_accept: The TLS/SSL handshake was not " "successful", __func__); return ERR; case 1: debug("%s: SSL_accept: The TLS/SSL handshake was " "successfully completed", __func__); loop = false; break; default: if (SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ || SSL_get_error(ssl, ret) == SSL_ERROR_WANT_WRITE) { (void) napms(100); continue; } debug("%s: SSL_accept: The TLS/SSL handshake was not " "successful because a fatal error occurred", __func__); return ERR; } } return OK; } static void send_doit(SSL *ssl, dcc_send *send_obj) { while (atomic_load_bool(&tls_server::accepting_new_connections) && send_obj->bytes_rem > 0) { char buf[DCC_IO_BYTES] = { '\0' }; int bytes; size_t bytes_read; static const int bufsize = static_cast(sizeof buf); bytes = ((send_obj->bytes_rem < bufsize) ? static_cast(send_obj->bytes_rem) : bufsize); if (!isValid(send_obj->fileptr)) break; bytes_read = fread(buf, 1, bytes, send_obj->fileptr); if (bytes_read == 0) { printtext_print("err", _("%s: file read error"), __func__); break; } else if (send_bytes(ssl, addrof(buf[0]), static_cast(bytes_read), send_obj->bytes_rem) != OK) { printtext_print("err", _("%s: tls write error"), __func__); break; } } } static void warn_and_shutdown(SSL *ssl, const char *func, const char *msg) { printtext_print("warn", "%s: %s", func, msg); dcc::shutdown_conn(ssl); } static void print_start_and_stop(const char *type, const char *start, const char *stop) { printtext_print(type, _("started: %s"), start); printtext_print(type, _("stopped: %s"), stop); } void dcc::handle_incoming_conn(SSL *ssl) { block_signals(); if (accept_incoming(ssl) != OK) { dcc::shutdown_conn(ssl); return; } std::string nick(""); std::string filename(""); if (read_request(ssl, nick, filename) != OK) { warn_and_shutdown(ssl, __func__, _("read request error")); return; } std::vector::size_type pos = 0; if (!find_send_obj(nick, filename.c_str(), pos)) { warn_and_shutdown(ssl, __func__, _("unable to find the send " "object")); return; } dcc_send *send_obj = addrof(send_db[pos]); if (send_obj->has_completed()) { warn_and_shutdown(ssl, __func__, _("already sent file")); return; } else if (send_obj->is_locked()) { warn_and_shutdown(ssl, __func__, _("the send object is in a " "locked state")); return; } else if (send_obj->fileptr == nullptr && (send_obj->fileptr = xfopen(send_obj->full_path.c_str(), "rb")) == nullptr) { warn_and_shutdown(ssl, __func__, _("file open error")); return; } send_obj->set_lock(1); send_obj->start = time(nullptr); send_doit(ssl, send_obj); send_obj->stop = time(nullptr); fclose_and_null(addrof(send_obj->fileptr)); std::string str1(""); std::string str2(""); get_time(str1, send_obj->start); get_time(str2, send_obj->stop); if (send_obj->has_completed()) { printtext_print("success", _("%s: successfully sent file: %s"), __func__, filename.c_str()); print_start_and_stop("success", str1.c_str(), str2.c_str()); } else { printtext_print("err", _("%s: file transfer incomplete: %s"), __func__, filename.c_str()); print_start_and_stop("err", str1.c_str(), str2.c_str()); dcc::shutdown_conn(ssl); return; } while (ssl != nullptr && !(SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN)) (void) napms(100); dcc::shutdown_conn(ssl); send_db.erase(send_db.begin() + pos); } void dcc::shutdown_conn(SSL *ssl) { if (ssl == nullptr) return; if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) { debug("%s: already sent shutdown", __func__); return; } switch (SSL_shutdown(ssl)) { case 0: debug("%s: SSL_shutdown: not yet finished", __func__); (void) SSL_shutdown(ssl); break; case 1: /* success! */ break; default: err_log(0, "%s: SSL_shutdown: error", __func__); break; } } bool dcc::want_unveil_uploads(void) { const char *dir = Config("dcc_upload_dir"); size_t len1, len2; if (strings_match(dir, "")) return false; len1 = strlen(dir); len2 = strlen(g_home_dir); if (len1 >= len2 && strncmp(dir, g_home_dir, MIN(len1, len2)) == STRINGS_MATCH) return false; return true; } swirc-3.5.5/src/commands/dcc.h000066400000000000000000000051011501213070300161320ustar00rootroot00000000000000#ifndef CMD_DCC_H #define CMD_DCC_H #include #include #if WIN32 #include /* DWORD */ #include #endif #include /* uint32_t */ #ifdef __cplusplus #include #endif #include #include "../textBuffer.h" #if defined(UNIX) #define PATH_SEP '/' #elif defined(WIN32) #define PATH_SEP '\\' #endif #if defined(UNIX) && !defined(_SOCKET_DEFINED) #define _SOCKET_DEFINED 1 typedef int SOCKET; #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif #ifndef SOCKET_ERROR #define SOCKET_ERROR -1 #endif #define TO_DBL(_var) static_cast(_var) #ifdef __cplusplus //lint -sem(dcc_get::destroy,cleanup) class dcc_get { public: std::string nick; std::string filename; intmax_t filesize; intmax_t bytes_rem; double size; char unit; time_t start; time_t stop; dcc_get(); dcc_get(const char *, const char *, intmax_t, uint32_t, uint16_t); dcc_get(const dcc_get &); dcc_get &operator=(const dcc_get &); ~dcc_get(); void destroy(void); void finalize_download(void); void get_file(void); bool has_completed(void) const; bool is_locked(void) const; void set_lock(int); private: FILE *fileptr; SOCKET sock; SSL *ssl; SSL_CTX *ssl_ctx; uint32_t addr; uint16_t port; int lock; bool create_socket(void); bool create_ssl_ctx(void); bool create_ssl_obj(void); int request_file(void); }; #endif typedef enum { TYPE_symlink, TYPE_block_file, TYPE_character_file, TYPE_directory, TYPE_fifo, TYPE_regular_file, TYPE_socket, TYPE_other, TYPE_unknown } FileType; __SWIRC_BEGIN_DECLS extern const int g_one_kilo; extern const int g_one_meg; extern const int g_one_gig; //lint -sem(get_list_of_matching_dcc_cmds, r_null) double percentage(double part, double total); void list_dir(const char *); PTEXTBUF get_list_of_matching_dcc_cmds(const char *); void cmd_dcc(const char *); void dcc_init(void); void dcc_deinit(void); int imax_to_int(const char *, const intmax_t); __SWIRC_END_DECLS #ifdef __cplusplus namespace dcc { void add_file(const char *, const char *, const char *, const char *) NONNULL; NORETURN void exit_thread(void); void get_file_detached(dcc_get *); void get_file_size(const intmax_t, double &, char &); bool get_remote_addr(std::string &, uint32_t &); const char *get_upload_dir(void); void handle_incoming_conn(SSL *); #if defined(UNIX) void set_recv_timeout(SOCKET, const time_t); #elif defined(WIN32) void set_recv_timeout(SOCKET, const DWORD); #endif void shutdown_conn(SSL *); bool want_unveil_uploads(void); } #endif #endif swirc-3.5.5/src/commands/echo.c000066400000000000000000000006311501213070300163150ustar00rootroot00000000000000#include "common.h" #include "../printtext.h" #include "../strHand.h" #include "echo.h" /* * usage: /echo */ void cmd_echo(const char *data) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if (strings_match(data, "")) { printtext(&ctx, "/echo: missing arguments"); return; } ctx.spec_type = TYPE_SPEC1; printtext(&ctx, "%s", data); } swirc-3.5.5/src/commands/echo.h000066400000000000000000000001621501213070300163210ustar00rootroot00000000000000#ifndef CMD_ECHO_H #define CMD_ECHO_H __SWIRC_BEGIN_DECLS void cmd_echo(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/fetchdic.cpp000066400000000000000000000155261501213070300175210ustar00rootroot00000000000000/* fetchdic.cpp Copyright (C) 2023-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include "../errHand.h" #include "../filePred.h" #include "../interpreter.h" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "fetchdic.h" #include "theme.h" const char g_aff_suffix[AFF_SUFFIX_LEN] = ".aff"; const char g_dic_suffix[DIC_SUFFIX_LEN] = ".dic"; struct dictionary { dictionary(); explicit dictionary(const char *); std::string lang; std::string name; std::string date; std::string url; std::string author; std::string license; void fetch(void) const; }; dictionary::dictionary() { this->lang.assign(""); this->name.assign(""); this->date.assign(""); this->url.assign(""); this->author.assign(""); this->license.assign(""); } dictionary::dictionary(const char *line) { char *last = const_cast(""); char *line_copy = sw_strdup(line); const char *token[6] = { nullptr }; static const char sep[] = "|"; token[0] = strtok_r(line_copy, sep, &last); /* lang */ token[1] = strtok_r(nullptr, sep, &last); /* name */ token[2] = strtok_r(nullptr, sep, &last); /* date */ token[3] = strtok_r(nullptr, sep, &last); /* url */ token[4] = strtok_r(nullptr, sep, &last); /* author */ token[5] = strtok_r(nullptr, sep, &last); /* license */ if (token[0] == nullptr || token[1] == nullptr || token[2] == nullptr || token[3] == nullptr || token[4] == nullptr || token[5] == nullptr) { free(line_copy); throw std::runtime_error("missing tokens"); } try { this->lang.assign(token[0]); this->name.assign(token[1]); this->date.assign(token[2]); this->url.assign(token[3]); this->author.assign(token[4]); this->license.assign(token[5]); } catch (...) { /* null */; } free(line_copy); } void dictionary::fetch(void) const { std::string aff_file(g_home_dir); std::string aff_url(this->url); std::string dic_file(g_home_dir); std::string dic_url(this->url); aff_file.append(SLASH).append(this->name).append(g_aff_suffix); dic_file.append(SLASH).append(this->name).append(g_dic_suffix); if (strstr(aff_file.c_str(), "..") || strstr(dic_file.c_str(), "..")) { printtext_print("err", "Possible path traversal detected. " "Cannot continue."); return; } aff_url.append(this->name).append(g_aff_suffix); dic_url.append(this->name).append(g_dic_suffix); printtext_print(nullptr, " - Fetching..."); url_to_file(aff_url.c_str(), aff_file.c_str()); if (is_regular_file(aff_file.c_str())) printtext_print("success", "Fetched %s", aff_file.c_str()); else printtext_print("err", "Error %s", g_aff_suffix); printtext_print(nullptr, " - Fetching..."); url_to_file(dic_url.c_str(), dic_file.c_str()); if (is_regular_file(dic_file.c_str())) printtext_print("success", "Fetched %s", dic_file.c_str()); else printtext_print("err", "Error %s", g_dic_suffix); printtext_print(nullptr, " - Completed"); } static void dump_db(const std::vector &vec) { for (const dictionary &dic : vec) { printtext_print("sp1", "----- %s%s%s -----", COLOR1, dic.lang.c_str(), TXT_NORMAL); printtext_print("sp1", "name: %s", dic.name.c_str()); printtext_print("sp1", "date: %s", dic.date.c_str()); printtext_print("sp1", "url: %s", dic.url.c_str()); printtext_print("sp1", "author: %s", dic.author.c_str()); printtext_print("sp1", "license: %s", dic.license.c_str()); printtext_print("sp1", " "); } } static void fetchdic(const char *name, std::vector &vec) { for (const dictionary &dic : vec) { if (strings_match(name, dic.name.c_str())) dic.fetch(); } } static read_result_t read_db(const char *path, std::vector &vec) { FILE *fp = nullptr; char *line = nullptr; read_result_t res = READ_INCOMPLETE; if (path == nullptr || (fp = xfopen(path, "r")) == nullptr) return FOPEN_FAILED; while (get_next_line_from_file(fp, &line)) { const char *cp = trim(&line[0]); adv_while_isspace(&cp); if (strings_match(cp, "") || *cp == '#') continue; try { dictionary dic(cp); #if defined(__cplusplus) && __cplusplus >= 201103L vec.emplace_back(dic); #else vec.push_back(dic); #endif } catch (...) { fclose(fp); free(line); return PARSE_ERROR; } } res = (feof(fp) ? READ_DB_OK : READ_INCOMPLETE); fclose(fp); free(line); return res; } /* * usage: /fetchdic [name] */ void cmd_fetchdic(const char *data) { const std::string cmd("/fetchdic"); std::string tmp(""); std::string url(""); std::vector vec; tmp.assign(g_tmp_dir).append(SLASH).append("dic_db"); url.assign(g_swircWebAddr).append("dic_db"); url_to_file(url.c_str(), tmp.c_str()); switch (read_db(tmp.c_str(), vec)) { case FOPEN_FAILED: printtext_print("err", "%s: cannot open database", cmd.c_str()); return; case PARSE_ERROR: printtext_print("err", "%s: failed to read database", cmd.c_str()); return; case READ_INCOMPLETE: printtext_print("err", "%s: end-of-file indicator not set", cmd.c_str()); return; case READ_DB_OK: default: /* No statements here ATM. */ break; } if (strings_match(data, "")) dump_db(vec); else fetchdic(data, vec); if (remove(tmp.c_str()) != 0) { err_log(errno, "%s: failed to remove: %s", __func__, tmp.c_str()); } } swirc-3.5.5/src/commands/fetchdic.h000066400000000000000000000004341501213070300171560ustar00rootroot00000000000000#ifndef FETCH_DICTIONARY_H #define FETCH_DICTIONARY_H #define AFF_SUFFIX_LEN 5 #define DIC_SUFFIX_LEN 5 __SWIRC_BEGIN_DECLS extern const char g_aff_suffix[AFF_SUFFIX_LEN]; extern const char g_dic_suffix[DIC_SUFFIX_LEN]; void cmd_fetchdic(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/ftp-unix.cpp000066400000000000000000000057621501213070300175230ustar00rootroot00000000000000/* The FTP command -- Unix specific functions Copyright (C) 2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "../assertAPI.h" #include "../errHand.h" #include "../printtext.h" #include "../sig.h" #include "../strHand.h" #include "atomicops.h" #include "ftp.h" static void * cmd_doit(void *arg) { STRING name = static_cast(arg); if (atomic_load_bool(&ftp::cmd_in_progress)) { printtext_print("err", "Command already in progress..."); free(name); ftp::exit_thread(); } (void) atomic_swap_bool(&ftp::cmd_in_progress, true); block_signals(); SELECT_AND_RUN_CMD(); free(name); (void) atomic_swap_bool(&ftp::cmd_in_progress, false); ftp::exit_thread(); /* NOTREACHED */ return nullptr; } NORETURN void ftp::exit_thread(void) { int dummy = 0; pthread_exit(&dummy); sw_assert_not_reached(); } void ftp::do_cmd_detached(CSTRING cmd) { pthread_t tid; if ((errno = pthread_create(&tid, nullptr, cmd_doit, sw_strdup(cmd))) != 0) err_sys("%s: pthread_create", __func__); else if ((errno = pthread_detach(tid)) != 0) err_sys("%s: pthread_detach", __func__); } void ftp::set_timeout(SOCKET sock, int optname, const time_t seconds) { struct timeval tv = { 0 }; if (optname != SO_RCVTIMEO && optname != SO_SNDTIMEO) { err_log(0, "%s: illegal option name", __func__); return; } tv.tv_sec = seconds; tv.tv_usec = 0; errno = 0; if (setsockopt(sock, SOL_SOCKET, optname, &tv, sizeof tv) != 0) err_log(errno, "%s: setsockopt", __func__); } swirc-3.5.5/src/commands/ftp-w32.cpp000066400000000000000000000057571501213070300171570ustar00rootroot00000000000000/* The FTP command -- W32 specific functions Copyright (C) 2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "../assertAPI.h" #include "../errHand.h" #include "../printtext.h" #include "../sig.h" #include "../strHand.h" #include "../tls-server.h" #include "../events/welcome-w32.h" /* dword_product() */ #include "atomicops.h" #include "ftp.h" typedef void __cdecl VoidCdecl; static VoidCdecl cmd_doit(void *arg) { STRING name = static_cast(arg); if (atomic_load_bool(&ftp::cmd_in_progress)) { printtext_print("err", "Command already in progress..."); free(name); ftp::exit_thread(); } (void) atomic_swap_bool(&ftp::cmd_in_progress, true); block_signals(); SELECT_AND_RUN_CMD(); free(name); (void) atomic_swap_bool(&ftp::cmd_in_progress, false); ftp::exit_thread(); } NORETURN void ftp::exit_thread(void) { _endthread(); sw_assert_not_reached(); } void ftp::do_cmd_detached(CSTRING cmd) { if (_beginthread(cmd_doit, 0, sw_strdup(cmd)) == g_beginthread_failed) err_sys("%s: _beginthread", __func__); } void ftp::set_timeout(SOCKET sock, int optname, const DWORD seconds) { const DWORD timeout_milliseconds = dword_product(seconds, 1000); const int optlen = static_cast(sizeof(DWORD)); if (optname != SO_RCVTIMEO && optname != SO_SNDTIMEO) { err_log(0, "%s: illegal option name", __func__); return; } if (setsockopt(sock, SOL_SOCKET, optname, reinterpret_cast (&timeout_milliseconds), optlen) != 0) err_log(0, "%s: setsockopt", __func__); } swirc-3.5.5/src/commands/ftp.cpp000066400000000000000000001005121501213070300165270ustar00rootroot00000000000000/* The FTP command Copyright (C) 2024-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #if __OpenBSD__ #include #endif #include #ifdef WIN32 #include #endif #include #include #include "../assertAPI.h" #include "../config.h" #include "../dataClassify.h" #include "../errHand.h" #include "../filePred.h" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../strdup_printf.h" #include "../theme.h" #include "connect.h" #include "dcc.h" /* list_dir() */ #include "ftp.h" #include "i18n.h" #ifdef WIN32 #define stat _stat #endif #define RECV_AND_CHECK(p_microsec)\ do {\ int m_bytes_received;\ struct network_recv_context m_recv_ctx(this->sock, 0, timeo,\ p_microsec);\ \ BZERO(this->buf, sizeof this->buf);\ m_bytes_received = net_recv_plain(&m_recv_ctx, this->buf,\ sizeof this->buf - 1);\ \ if (m_bytes_received <= 0)\ return 0;\ if (memchr(this->buf, 0, m_bytes_received) != nullptr)\ destroy_null_bytes_exported(this->buf,\ m_bytes_received);\ if (strpbrk(this->buf, sep) == nullptr)\ return 0;\ } while (false) ftp_ctl_conn *ftp::ctl_conn = nullptr; ftp_data_conn *ftp::data_conn = nullptr; volatile bool ftp::cmd_in_progress = false; volatile bool ftp::loop_get_file = false; volatile bool ftp::loop_send_file = false; static stringarray_t ftp_cmds = { "cd ", "del ", "exit", "get ", "login", "ls ", "ls dir", "ls up", "ls down", "mkdir ", "pwd", "rmdir ", "send ", "system", }; static void delete_data_conn(void); static void print_one_rep(const int, CSTRING); ftp_ctl_conn::ftp_ctl_conn() : sock(INVALID_SOCKET) , state(CONCAT_BUFFER_IS_EMPTY) , res(nullptr) { BZERO(this->buf, sizeof this->buf); this->message_concat.assign(""); } ftp_ctl_conn::~ftp_ctl_conn() { if (this->sock != INVALID_SOCKET) { ftp_closesocket(this->sock); this->sock = INVALID_SOCKET; } if (this->res != nullptr) { freeaddrinfo(this->res); this->res = nullptr; } } SOCKET ftp_ctl_conn::get_sock(void) const { return (this->sock); } static bool establish_conn(SOCKET &sock, const struct addrinfo *res) { for (const struct addrinfo *rp = res; rp; rp = rp->ai_next) { if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == INVALID_SOCKET) continue; ftp::set_timeout(sock, SO_RCVTIMEO, FTP_TEMP_RECV_TIMEOUT); ftp::set_timeout(sock, SO_SNDTIMEO, FTP_TEMP_SEND_TIMEOUT); if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0) { ftp::set_timeout(sock, SO_RCVTIMEO, FTP_DEFAULT_RECV_TIMEOUT); ftp::set_timeout(sock, SO_SNDTIMEO, FTP_DEFAULT_SEND_TIMEOUT); return true; } else { ftp_closesocket(sock); sock = INVALID_SOCKET; } } /* for */ return false; } void ftp_ctl_conn::login(void) { CSTRING array[2]; const size_t PASSMAX = 100; immutable_cp_t host = Config("ftp_host"); immutable_cp_t port = Config("ftp_port"); int n_sent; long int dummy = 0; array[0] = Config("ftp_user"); array[1] = Config("ftp_pass"); try { if (!is_valid_username(array[0])) { throw std::runtime_error(_("Invalid username")); } else if (strlen(array[1]) > PASSMAX) { throw std::runtime_error(_("Too long password")); } else if (!is_valid_hostname(host)) { throw std::runtime_error(_("Invalid hostname")); } else if (!getval_strtol(port, 1, 65535, &dummy)) { throw std::runtime_error(_("Invalid port number")); } else if ((this->res = net_addr_resolve(host, port)) == nullptr) { throw std::runtime_error(_("Unable to get a list of " "IP addresses")); } else if (!establish_conn(this->sock, this->res)) { throw std::runtime_error(_("Failed to establish a " "connection")); } this->read_and_print(1); ftp::set_timeout(sock, SO_SNDTIMEO, 4); n_sent = ftp::send_printf(this->sock, "USER %s\r\nPASS %s\r\n", array[0], array[1]); if (n_sent <= 0) throw std::runtime_error(_("Cannot send")); this->read_and_print(1); } catch (const std::exception &ex) { immutable_cp_t b1 = LEFT_BRKT; immutable_cp_t b2 = RIGHT_BRKT; printtext_print("err", "%s%s%s %s: %s", b1, "FTP", b2, __func__, ex.what()); } } static void print_one_rep(const int num, CSTRING text) { immutable_cp_t b1 = Theme("notice_inner_b1"); immutable_cp_t b2 = Theme("notice_inner_b2"); immutable_cp_t sep = Theme("notice_sep"); if (num == 125 || num == 150) return; printtext_print("none", "%s%s%s%d%s %s", b1, "FTP", sep, num, b2, text); } void ftp_ctl_conn::printreps(void) { for (const FTP_REPLY &rep : this->reply_vec) print_one_rep(rep.num, rep.text.c_str()); } void ftp_ctl_conn::read_and_print(const int timeo) { if (this->sock == INVALID_SOCKET) return; while (this->read_reply(timeo)) this->printreps(); } static bool is_terminated(CSTRING buf, const size_t size) { size_t last; if ((last = strlen(buf) - 1) >= size) err_exit(EOVERFLOW, "%s", __func__); return (buf[last] == '\r' || buf[last] == '\n'); } static std::string get_last_token(CSTRING buffer) { CSTRING last_token; if ((last_token = strrchr(buffer, '\n')) == nullptr) err_quit("%s: unable to locate newline", __func__); std::string out(++last_token); return out; } static inline bool has_three_digits(CSTRING str) { return (sw_isdigit(str[0]) && sw_isdigit(str[1]) && sw_isdigit(str[2])); } static void handle_length_four(char (&numstr)[5], CSTRING token, const size_t len, std::vector &reply_vec) { char ch = 'a'; int num = 0; memcpy(numstr, token, len); numstr[len] = '\0'; errno = 0; if (!has_three_digits(numstr)) { err_log(0, "%s: expected digits", __func__); } #ifdef HAVE_BCI else if (sscanf_s(numstr, "%d%c", &num, &ch, (UINT)sizeof(char)) != 2 || ch != '-') { err_log(errno, "%s: sscanf_s() error", __func__); } #else else if (sscanf(numstr, "%d%c", &num, &ch) != 2 || ch != '-') { err_log(0, "%s: sscanf() error", __func__); } #endif else { FTP_REPLY rep(num, token + len); #if defined(__cplusplus) && __cplusplus >= 201103L reply_vec.emplace_back(rep); #else reply_vec.push_back(rep); #endif } } static void handle_length_three(char (&numstr)[5], CSTRING token, const size_t len, std::vector &reply_vec) { int num = 0; memcpy(numstr, token, len); numstr[len] = '\0'; if (!has_three_digits(numstr)) { err_log(0, "%s: expected digits", __func__); } else if (xsscanf(numstr, "%d", &num) != 1) { err_log(0, "%s: sscanf() error", __func__); } else { FTP_REPLY rep(num, token + len); #if defined(__cplusplus) && __cplusplus >= 201103L reply_vec.emplace_back(rep); #else reply_vec.push_back(rep); #endif } } static void process_reply(CSTRING token, std::vector &reply_vec) { char numstr[5] = { '\0' }; const size_t len = strspn(token, "0123456789-"); if (strspn(token, "bcdlrswx-") == 10) { FTP_REPLY rep(1000, token); #if defined(__cplusplus) && __cplusplus >= 201103L reply_vec.emplace_back(rep); #else reply_vec.push_back(rep); #endif } else if (len >= sizeof numstr) { err_log(0, "%s: initial segment too long", __func__); } else if (len == 4) { handle_length_four(numstr, token, len, reply_vec); } else if (len == 3) { handle_length_three(numstr, token, len, reply_vec); } else if (len == 0) { FTP_REPLY rep(0, token); #if defined(__cplusplus) && __cplusplus >= 201103L reply_vec.emplace_back(rep); #else reply_vec.push_back(rep); #endif } else { err_log(0, "%s: unexpected length %zu: \"%s\"", __func__, len, token); } } static inline bool shall_assign_concat(CSTRING token, const std::string &last_token, const enum message_concat_state state) { return (!last_token.empty() && state == CONCAT_BUFFER_IS_EMPTY && strings_match(last_token.c_str(), token)); } static inline bool shall_append_concat(const int loop_run, const enum message_concat_state state) { return (loop_run == 0 && state == CONCAT_BUFFER_CONTAIN_DATA); } numrep_t ftp_ctl_conn::read_reply(const int timeo) { CSTRING token; STRING tokstate = const_cast(""); int loop_run = 0; static chararray_t sep = "\r\n"; std::string last_token(""); if (this->sock == INVALID_SOCKET) return 0; RECV_AND_CHECK(345000); this->reply_vec.clear(); if (!is_terminated(this->buf, sizeof this->buf)) last_token.assign(get_last_token(this->buf)); if (this->state == CONCAT_BUFFER_CONTAIN_DATA && this->buf[0] == '\r' && this->buf[1] == '\n') { process_reply(this->message_concat.c_str(), this->reply_vec); this->message_concat.assign(""); this->state = CONCAT_BUFFER_IS_EMPTY; } for (STRING str = this->buf; (token = strtok_r(str, sep, &tokstate)) != nullptr; str = nullptr) { if (shall_assign_concat(token, last_token, this->state)) { this->message_concat.assign(last_token); this->state = CONCAT_BUFFER_CONTAIN_DATA; return (this->reply_vec.size()); } else if (shall_append_concat(loop_run, this->state)) { this->message_concat.append(token); this->state = CONCAT_BUFFER_IS_EMPTY; token = this->message_concat.c_str(); } process_reply(token, this->reply_vec); loop_run++; } /* for */ return (this->reply_vec.size()); } ftp_data_conn::ftp_data_conn() : full_path(nullptr) , path(nullptr) , filesz(-1) , fileptr(nullptr) , sock(INVALID_SOCKET) , host_str(nullptr) , port_str(nullptr) , state(CONCAT_BUFFER_IS_EMPTY) , res(nullptr) , port(0) { BZERO(this->buf, sizeof this->buf); this->message_concat.assign(""); this->h[0] = this->h[1] = this->h[2] = this->h[3] = 0; this->p[0] = this->p[1] = 0; } ftp_data_conn::ftp_data_conn(CSTRING text) : full_path(nullptr) , path(nullptr) , filesz(-1) , fileptr(nullptr) , sock(INVALID_SOCKET) , host_str(nullptr) , port_str(nullptr) , state(CONCAT_BUFFER_IS_EMPTY) , res(nullptr) , port(0) { static CSTRING format = " Entering Passive Mode " "(%hhu,%hhu,%hhu,%hhu,%hhu,%hhu)."; if (xsscanf(text, format, &this->h[0], &this->h[1], &this->h[2], &this->h[3], &this->p[0], &this->p[1]) != 6) throw std::runtime_error("too few items assigned"); BZERO(this->buf, sizeof this->buf); this->host_str = strdup_printf("%u.%u.%u.%u", this->h[0], this->h[1], this->h[2], this->h[3]); this->port = (this->p[0] * 256 + this->p[1]); this->port_str = strdup_printf("%u", this->port); this->message_concat.assign(""); } ftp_data_conn::~ftp_data_conn() { /* * Close first... */ if (this->sock != INVALID_SOCKET) { ftp_closesocket(this->sock); this->sock = INVALID_SOCKET; } free(this->full_path); free(this->path); if (this->fileptr != nullptr) { fclose(this->fileptr); this->fileptr = nullptr; } free(this->host_str); free(this->port_str); if (this->res != nullptr) { freeaddrinfo(this->res); this->res = nullptr; } } bool ftp_data_conn::connect_passive(void) { struct sockaddr_in sin; if (this->sock != INVALID_SOCKET) ftp_closesocket(this->sock); if ((this->sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { printtext_print("err", "%s: %s", __func__, _("Failed to create an endpoint for communication")); return false; } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(this->port); sin.sin_addr.s_addr = inet_addr(this->host_str); if (connect(this->sock, reinterpret_cast(&sin), sizeof sin) != 0) { printtext_print("err", "%s: %s", __func__, _("Failed to establish a connection")); return false; } return true; } static bool zero_truncate(FILE *fileptr) { #if defined(UNIX) return (ftruncate(fileno(fileptr), 0) == 0); #elif defined(WIN32) return ((errno = _chsize_s(fileno(fileptr), 0)) == 0); #endif } static void get_bytes(const std::string &str) { CSTRING bytes_str; STRING str_copy = sw_strdup(str.c_str()); STRING tokstate = const_cast(""); static chararray_t sep = " \r\n"; (void) strtok_r(str_copy, sep, &tokstate); (void) strtok_r(nullptr, sep, &tokstate); (void) strtok_r(nullptr, sep, &tokstate); (void) strtok_r(nullptr, sep, &tokstate); if ((bytes_str = strtok_r(nullptr, sep, &tokstate)) == nullptr || !is_numeric(bytes_str) || xsscanf(bytes_str, "%jd", &ftp::data_conn->filesz) != 1) { printtext_print("warn", "failed to get the file size"); ftp::data_conn->filesz = -1; } free(str_copy); } static void print_complete(CSTRING path, double part, double total, bool (&state)[3]) { double val; if (state[2]) return; val = percentage(part, total); if (val >= 75.0 && !state[2]) { printtext_print("success", "%s: %.2f%% complete", path, val); state[2] = true; } else if (val >= 50.0 && !state[1]) { printtext_print("success", "%s: %.2f%% complete", path, val); state[1] = true; } else if (val >= 25.0 && !state[0]) { printtext_print("success", "%s: %.2f%% complete", path, val); state[0] = true; } else { napms(1); } } void ftp_data_conn::get_file(void) { bool proceed = true; bool printed[3] = { false }; char unit = 'B'; double size = 0.0; int bytes_received; intmax_t total = 0; while (ftp::ctl_conn->read_reply(1)) { for (const FTP_REPLY &rep : ftp::ctl_conn->reply_vec) { if (rep.num == 211 || rep.num == 213) { /* null */; } else if (rep.num == 450 || rep.num == 550) { proceed = false; print_one_rep(rep.num, rep.text.c_str()); } else if (rep.num == 1000) { get_bytes(rep.text); // print_one_rep(rep.num, rep.text.c_str()); } else { print_one_rep(rep.num, rep.text.c_str()); } } } if (!proceed) return; if (this->full_path == nullptr || this->path == nullptr) return; if ((this->fileptr = xfopen(this->full_path, "ab")) == nullptr) { printtext_print("err", "open failed: %s", this->full_path); return; } if (!zero_truncate(this->fileptr)) { printtext_print("err", "change size error"); return; } struct network_recv_context recv_ctx(this->sock, 0, 3, 0); if (this->filesz != -1) { dcc::get_file_size(this->filesz, size, unit); printtext_print("success", "getting: %s (%.1f%c)...", this->path, size, unit); } else printtext_print("success", "getting: %s...", this->path); (void) atomic_swap_bool(&ftp::loop_get_file, true); while (atomic_load_bool(&ftp::loop_get_file)) { if (!isValid(this->fileptr) || this->sock == INVALID_SOCKET) break; BZERO(this->buf, sizeof this->buf); bytes_received = net_recv_plain(&recv_ctx, this->buf, sizeof this->buf - 1); if (bytes_received > 0) { if (fwrite(this->buf, 1, bytes_received, this->fileptr) != static_cast(bytes_received)) { printtext_print("err", "file write error"); break; } else total += bytes_received; if (this->filesz != -1) { print_complete(this->path, TO_DBL(total), TO_DBL(this->filesz), printed); } } else if (bytes_received == 0) { /* continue */; } else if (bytes_received < 0) { break; } else { sw_assert_not_reached(); } } ftp::ctl_conn->read_and_print(1); fclose_and_null(&this->fileptr); dcc::get_file_size(total, size, unit); printtext_print("success", "wrote: %s (%.1f%c)", this->full_path, size, unit); } SOCKET ftp_data_conn::get_sock(void) const { return (this->sock); } numstr_t ftp_data_conn::list_fetch(const int timeo) { CSTRING token; STRING tokstate = const_cast(""); int loop_run = 0; static chararray_t sep = "\r\n"; std::string last_token(""); if (this->sock == INVALID_SOCKET) return 0; RECV_AND_CHECK(233000); this->vec.clear(); if (!is_terminated(this->buf, sizeof this->buf)) last_token.assign(get_last_token(this->buf)); if (this->state == CONCAT_BUFFER_CONTAIN_DATA && this->buf[0] == '\r' && this->buf[1] == '\n') { this->vec.push_back(this->message_concat); this->message_concat.assign(""); this->state = CONCAT_BUFFER_IS_EMPTY; } for (STRING str = this->buf; (token = strtok_r(str, sep, &tokstate)) != nullptr; str = nullptr) { if (shall_assign_concat(token, last_token, this->state)) { this->message_concat.assign(last_token); this->state = CONCAT_BUFFER_CONTAIN_DATA; return (this->vec.size()); } else if (shall_append_concat(loop_run, this->state)) { this->message_concat.append(token); this->state = CONCAT_BUFFER_IS_EMPTY; token = this->message_concat.c_str(); } this->vec.push_back(token); loop_run++; } /* for */ return (this->vec.size()); } void ftp_data_conn::list_print(void) { if (this->vec.empty()) return; for (const std::string &str : this->vec) print_one_rep(0, str.c_str()); // XXX } void ftp_data_conn::send_file(void) { bool printed[3] = { false }; bool proceed = true; char unit = 'B'; double size = 0.0; int bytes_sent = 0; intmax_t total = 0; size_t bytes = 0; size_t bytes_read = 0; uintmax_t bytes_rem = this->filesz; while (ftp::ctl_conn->read_reply(1)) { for (const FTP_REPLY &rep : ftp::ctl_conn->reply_vec) { if (rep.num == 421 || rep.num == 450 || rep.num == 452 || rep.num == 500 || rep.num == 501 || rep.num == 530 || rep.num == 532 || rep.num == 550 || rep.num == 553) proceed = false; print_one_rep(rep.num, rep.text.c_str()); } } if (!proceed) return; if (this->full_path == nullptr || this->path == nullptr) return; if ((this->fileptr = xfopen(this->full_path, "rb")) == nullptr) { printtext_print("err", "open failed: %s", this->full_path); return; } dcc::get_file_size(this->filesz, size, unit); printtext_print("success", "sending: %s (%.1f%c)...", this->path, size, unit); (void) atomic_swap_bool(&ftp::loop_send_file, true); while (atomic_load_bool(&ftp::loop_send_file) && bytes_rem > 0 && total != this->filesz) { static const size_t bufsize = sizeof this->buf; bytes = (bytes_rem < bufsize ? bytes_rem : bufsize); if (!isValid(this->fileptr) || this->sock == INVALID_SOCKET) break; BZERO(this->buf, bufsize); if ((bytes_read = fread(this->buf, 1, bytes, this->fileptr)) == 0) { printtext_print("err", _("%s: file read error"), __func__); break; } else if ((bytes_sent = ftp::send_bytes(this->sock, this->buf, size_to_int(bytes_read))) <= 0) { break; } else if (static_cast(bytes_sent) != bytes_read) { printtext_print("err", _("%s: bytes sent mismatch " "bytes read"), __func__); break; } else { bytes_rem -= bytes_sent; total += bytes_sent; print_complete(this->path, TO_DBL(total), TO_DBL(this->filesz), printed); } } ftp::ctl_conn->read_and_print(1); fclose_and_null(&this->fileptr); dcc::get_file_size(total, size, unit); if (bytes_rem > 0 || total != this->filesz) { printtext_print("warn", "sent: %s (%.1f%c, incomplete)", this->path, size, unit); } else { printtext_print("success", "sent: %s (%.1f%c)", this->path, size, unit); } } static bool subcmd_ok(CSTRING cmd) { if (strings_match(cmd, "cd")) return true; else if (strings_match(cmd, "del")) return true; else if (strings_match(cmd, "exit")) return true; else if (strings_match(cmd, "get")) return true; else if (strings_match(cmd, "login")) return true; else if (strings_match(cmd, "ls")) return true; else if (strings_match(cmd, "mkdir")) return true; else if (strings_match(cmd, "pwd")) return true; else if (strings_match(cmd, "rmdir")) return true; else if (strings_match(cmd, "send")) return true; else if (strings_match(cmd, "system")) return true; return false; } static void perform_ftp_cmd(CSTRING cmd, CSTRING arg) { int n_sent; if (arg == nullptr || strings_match(arg, "")) { printtext_print("err", "%s", _("Insufficient arguments")); return; } else if (ftp::ctl_conn == nullptr) { printtext_print("err", "%s", _("No control connection")); return; } else if (ftp::ctl_conn->get_sock() == INVALID_SOCKET) { printtext_print("err", "%s", _("Invalid network socket")); return; } n_sent = ftp::send_printf(ftp::ctl_conn->get_sock(), "%s %s\r\n", cmd, arg); if (n_sent <= 0) { printtext_print("err", "%s", _("Cannot send")); return; } ftp::ctl_conn->read_and_print(0); } static void perform_simple_ftp_cmd(CSTRING cmd) { int n_sent; if (ftp::ctl_conn == nullptr) return; else if (ftp::ctl_conn->get_sock() == INVALID_SOCKET) { printtext_print("err", "%s", _("Invalid network socket")); return; } n_sent = ftp::send_printf(ftp::ctl_conn->get_sock(), "%s", cmd); if (n_sent <= 0) return; ftp::ctl_conn->read_and_print(0); } static void subcmd_cd(CSTRING pathname) { perform_ftp_cmd("CWD", pathname); } static void subcmd_del(CSTRING pathname) { perform_ftp_cmd("DELE", pathname); } static void subcmd_exit(void) { if (ftp::ctl_conn == nullptr) return; ftp::set_timeout(ftp::ctl_conn->get_sock(), SO_SNDTIMEO, 1); if (ftp::data_conn) { (void) ftp::send_printf(ftp::ctl_conn->get_sock(), "ABOR\r\nQUIT\r\n"); ftp::shutdown_sock(ftp::data_conn->get_sock()); (void) atomic_swap_bool(&ftp::loop_get_file, false); (void) atomic_swap_bool(&ftp::loop_send_file, false); while (atomic_load_bool(&ftp::cmd_in_progress)) napms(1); delete_data_conn(); } else { (void) ftp::send_printf(ftp::ctl_conn->get_sock(), "QUIT\r\n"); } ftp::ctl_conn->read_and_print(1); delete ftp::ctl_conn; ftp::ctl_conn = nullptr; } static void subcmd_get(CSTRING path) { int n_sent; if (path == nullptr || strings_match(path, "")) { printtext_print("err", "%s", _("Insufficient arguments")); return; } else if (is_whitespace(path)) { printtext_print("err", "%s", _("Blank file path")); return; } else if (!ftp::passive()) { return; } else if (!ftp::data_conn->connect_passive()) { delete_data_conn(); return; } ftp::data_conn->full_path = strdup_printf("%s%s%s", g_ftp_download_dir, SLASH, path); ftp::data_conn->path = sw_strdup(path); n_sent = ftp::send_printf(ftp::ctl_conn->get_sock(), "STAT %s\r\nTYPE L 8\r\nRETR %s\r\n", path, path); if (n_sent <= 0) { delete_data_conn(); return; } ftp::do_cmd_detached("get file"); } static void subcmd_login(void) { if (ftp::ctl_conn) { printtext_print("err", "A control connection already exists. " "/ftp exit?"); return; } ftp::do_cmd_detached("login"); } static void delete_data_conn(void) { delete ftp::data_conn; ftp::data_conn = nullptr; } static void subcmd_ls(CSTRING arg) { if (arg == nullptr || strings_match(arg, "")) { printtext_print("err", "%s", _("Insufficient arguments")); } else if (strings_match(arg, "dir")) { ftp::do_cmd_detached("ls dir"); } else if (strings_match(arg, "up")) { list_dir(ftp::get_upload_dir()); } else if (strings_match(arg, "down")) { list_dir(g_ftp_download_dir); } else { printtext_print("err", "what? dir, uploads or downloads?"); } } static void subcmd_mkdir(CSTRING path) { perform_ftp_cmd("MKD", path); } static void subcmd_pwd(void) { perform_simple_ftp_cmd("PWD\r\n"); } static void subcmd_rmdir(CSTRING path) { perform_ftp_cmd("RMD", path); } static void subcmd_send(CSTRING path) { int n_sent; struct stat sb = { 0 }; if (path == nullptr || strings_match(path, "")) { printtext_print("err", "%s", _("Insufficient arguments")); return; } else if (is_whitespace(path)) { printtext_print("err", "%s", _("Blank file path")); return; } else if (!ftp::passive()) { return; } else if (!ftp::data_conn->connect_passive()) { delete_data_conn(); return; } ftp::data_conn->full_path = strdup_printf("%s%s%s", ftp::get_upload_dir(), SLASH, path); ftp::data_conn->path = sw_strdup(path); if (!is_regular_file(ftp::data_conn->full_path)) { printtext_print("err", "%s: file doesn't exist or isn't a " "regular file", ftp::data_conn->full_path); delete_data_conn(); return; } else if (check_path(ftp::get_upload_dir(), ftp::data_conn->full_path) == ERR) { printtext_print("err", "check path error"); delete_data_conn(); return; } else if (stat(ftp::data_conn->full_path, &sb) != 0) { printtext_print("err", "%s: couldn't stat file", ftp::data_conn->full_path); delete_data_conn(); return; } else if (sb.st_size == 0) { printtext_print("err", "%s: zero size", ftp::data_conn->full_path); delete_data_conn(); return; } else { ftp::data_conn->filesz = static_cast(sb.st_size); } n_sent = ftp::send_printf(ftp::ctl_conn->get_sock(), "TYPE L 8\r\nSTOR %s\r\n", path); if (n_sent <= 0) { delete_data_conn(); return; } ftp::do_cmd_detached("send file"); } static void subcmd_system(void) { perform_simple_ftp_cmd("SYST\r\n"); } /* * usage: * /ftp cd * /ftp del * /ftp exit * /ftp get * /ftp login * /ftp ls [dir|up|down] * /ftp mkdir * /ftp pwd * /ftp rmdir * /ftp send * /ftp system */ void cmd_ftp(CSTRING data) { CSTRING arg[2]; CSTRING subcmd; STRING dcopy; STRING last = const_cast(""); static chararray_t cmd = "/ftp"; static chararray_t sep = "\n"; static const size_t MAXARG = 300; if (strings_match(data, "")) { printtext_print("err", "%s", _("Insufficient arguments")); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 1); if ((subcmd = strtok_r(dcopy, sep, &last)) == nullptr) { printf_and_free(dcopy, "%s: insufficient args", cmd); return; } else if (!subcmd_ok(subcmd)) { printf_and_free(dcopy, "%s: invalid subcommand '%s'", cmd, subcmd); return; } if ((arg[0] = strtok_r(nullptr, sep, &last)) != nullptr) { if (strlen(arg[0]) > MAXARG) { printf_and_free(dcopy, "%s: arg too long (exceeds %zu)", cmd, MAXARG); return; } } if (strings_match(subcmd, "cd")) subcmd_cd(arg[0]); else if (strings_match(subcmd, "del")) subcmd_del(arg[0]); else if (strings_match(subcmd, "exit")) subcmd_exit(); else if (strings_match(subcmd, "get")) subcmd_get(arg[0]); else if (strings_match(subcmd, "login")) subcmd_login(); else if (strings_match(subcmd, "ls")) subcmd_ls(arg[0]); else if (strings_match(subcmd, "mkdir")) subcmd_mkdir(arg[0]); else if (strings_match(subcmd, "pwd")) subcmd_pwd(); else if (strings_match(subcmd, "rmdir")) subcmd_rmdir(arg[0]); else if (strings_match(subcmd, "send")) subcmd_send(arg[0]); else if (strings_match(subcmd, "system")) subcmd_system(); else printtext_print("err", "%s: invalid subcommand", cmd); free(dcopy); } void ftp_init(void) { debug("%s called", __func__); #ifdef WIN32 winsock_init_doit(); #endif } void ftp_deinit(void) { delete ftp::ctl_conn; ftp::ctl_conn = nullptr; delete ftp::data_conn; ftp::data_conn = nullptr; } PTEXTBUF get_list_of_matching_ftp_cmds(CSTRING search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(ftp_cmds); i++) { CSTRING cmd = ftp_cmds[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return nullptr; } return matches; } static void create_data_conn(CSTRING text) { if (ftp::data_conn) { delete ftp::data_conn; ftp::data_conn = nullptr; } try { ftp::data_conn = new ftp_data_conn(text); } catch (const std::bad_alloc &e) { err_exit(ENOMEM, "%s: %s", __func__, e.what()); } catch (const std::runtime_error &e) { printtext_print("err", "%s: %s", __func__, e.what()); } catch (...) { printtext_print("err", "%s: %s", __func__, "unknown error"); } } bool ftp::passive(void) { SOCKET sock; if (ftp::ctl_conn == nullptr) { printtext_print("err", "%s: %s", __func__, _("No control connection")); return false; } else if (ftp::data_conn != nullptr) { printtext_print("err", "%s: %s", __func__, _("Already in passive mode")); return false; } else if ((sock = ftp::ctl_conn->get_sock()) == INVALID_SOCKET) { printtext_print("err", "%s: %s", __func__, _("Invalid network socket")); return false; } else if (ftp::send_printf(sock, "PASV\r\n") <= 0) { printtext_print("err", "%s: %s", __func__, _("Cannot send")); return false; } while (ftp::ctl_conn->read_reply(0)) { for (const FTP_REPLY &rep : ftp::ctl_conn->reply_vec) { if (rep.num == 227) create_data_conn(rep.text.c_str()); else print_one_rep(rep.num, rep.text.c_str()); } } if (ftp::data_conn) return true; printtext_print("err", "%s", _("Failed to enter passive mode")); return false; } void ftp::get_file(void) { if (ftp::data_conn == nullptr) return; ftp::data_conn->get_file(); delete_data_conn(); } CSTRING ftp::get_upload_dir(void) { static CSTRING dir; dir = Config("ftp_upload_dir"); #if defined(OpenBSD) && OpenBSD >= 201811 return (strings_match(dir, "") ? g_ftp_upload_dir : dir); #else if (!is_directory(dir)) return (g_ftp_upload_dir ? g_ftp_upload_dir : ""); return dir; #endif } void ftp::login(void) { delete ftp::ctl_conn; ftp::ctl_conn = new ftp_ctl_conn(); ftp::ctl_conn->login(); } void ftp::ls_dir(void) { int n_sent; if (!ftp::passive()) return; if (!ftp::data_conn->connect_passive()) { delete_data_conn(); return; } n_sent = ftp::send_printf(ftp::ctl_conn->get_sock(), "LIST\r\n"); if (n_sent <= 0) { delete_data_conn(); return; } while (ftp::data_conn->list_fetch(3)) ftp::data_conn->list_print(); delete_data_conn(); ftp::ctl_conn->read_and_print(1); } int ftp::send_bytes(SOCKET sock, const void *buf, const int len) { int bytes_sent; errno = 0; #if defined(UNIX) if ((bytes_sent = send(sock, buf, len, 0)) == SOCKET_ERROR) return (errno == EAGAIN || errno == EWOULDBLOCK ? 0 : -1); #elif defined(WIN32) if ((bytes_sent = send(sock, static_cast(buf), len, 0)) == SOCKET_ERROR) return (WSAGetLastError() == WSAEWOULDBLOCK ? 0 : -1); #endif return bytes_sent; } void ftp::send_file(void) { if (ftp::data_conn == nullptr) return; ftp::data_conn->send_file(); delete_data_conn(); } int ftp::send_printf(SOCKET sock, CSTRING fmt, ...) { char *buffer; int n_sent; va_list ap; if (sock == INVALID_SOCKET) return -1; else if (fmt == nullptr) err_exit(EINVAL, "%s", __func__); else if (strings_match(fmt, "")) return 0; va_start(ap, fmt); buffer = strdup_vprintf(fmt, ap); va_end(ap); errno = 0; #if defined(UNIX) if ((n_sent = send(sock, buffer, strlen(buffer), 0)) == -1) { free_and_null(&buffer); return (errno == EAGAIN || errno == EWOULDBLOCK ? 0 : -1); } #elif defined(WIN32) if ((n_sent = send(sock, buffer, size_to_int(strlen(buffer)), 0)) == SOCKET_ERROR) { free_and_null(&buffer); return (WSAGetLastError() == WSAEWOULDBLOCK ? 0 : -1); } #endif free_and_null(&buffer); return n_sent; } void ftp::shutdown_sock(SOCKET sock) { if (sock == INVALID_SOCKET) return; #if defined(UNIX) if (shutdown(sock, SHUT_RDWR) == -1) err_log(errno, "%s: shutdown", __func__); #elif defined(WIN32) if (shutdown(sock, SD_BOTH) != 0) err_log(errno, "%s: shutdown", __func__); #endif ftp_closesocket(sock); } bool ftp::want_unveil_uploads(void) { CSTRING dir = Config("ftp_upload_dir"); size_t len1, len2; if (strings_match(dir, "")) return false; len1 = strlen(dir); len2 = strlen(g_home_dir); if (len1 >= len2 && strncmp(dir, g_home_dir, MIN(len1, len2)) == STRINGS_MATCH) return false; return true; } swirc-3.5.5/src/commands/ftp.h000066400000000000000000000120471501213070300162010ustar00rootroot00000000000000#ifndef SRC_COMMANDS_FTP_H_ #define SRC_COMMANDS_FTP_H_ /* ftp.h Copyright (C) 2024, 2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(UNIX) #include #include #include #include #include #include #elif defined(WIN32) #include /* DWORD */ #include #endif #ifdef __cplusplus #include #include #endif #include #include "../irc.h" #include "../textBuffer.h" #if defined(UNIX) && !defined(_SOCKET_DEFINED) #define _SOCKET_DEFINED 1 typedef int SOCKET; #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif #ifndef SOCKET_ERROR #define SOCKET_ERROR -1 #endif #if defined(UNIX) #define ftp_closesocket(_sock) ((void) close(_sock)) #elif defined(WIN32) #define ftp_closesocket(_sock) ((void) closesocket(_sock)) #endif #define FTP_DEFAULT_RECV_TIMEOUT 30 #define FTP_DEFAULT_SEND_TIMEOUT 15 #define FTP_TEMP_RECV_TIMEOUT 4 #define FTP_TEMP_SEND_TIMEOUT 4 #define SELECT_AND_RUN_CMD()\ do {\ if (strings_match(name, "get file"))\ ftp::get_file();\ else if (strings_match(name, "login"))\ ftp::login();\ else if (strings_match(name, "ls dir"))\ ftp::ls_dir();\ else if (strings_match(name, "send file"))\ ftp::send_file();\ else\ err_log(0, "%s: incorrect command: '%s'", __func__,\ name);\ } while (false) __SWIRC_BEGIN_DECLS void cmd_ftp(CSTRING); void ftp_init(void); void ftp_deinit(void); //lint -sem(get_list_of_matching_ftp_cmds, r_null) PTEXTBUF get_list_of_matching_ftp_cmds(CSTRING); __SWIRC_END_DECLS #ifdef __cplusplus typedef struct tagFTP_REPLY { int num; std::string text; tagFTP_REPLY() : num(0) { this->text.assign(""); } tagFTP_REPLY(int p_num, CSTRING p_text) : num(p_num) { this->text.assign(p_text); } } FTP_REPLY, *PFTP_REPLY; typedef std::vector::size_type numrep_t; typedef std::vector::size_type numstr_t; class ftp_ctl_conn { public: ftp_ctl_conn(); ~ftp_ctl_conn(); std::vector reply_vec; SOCKET get_sock(void) const; void login(void); void printreps(void); void read_and_print(const int); numrep_t read_reply(const int); private: SOCKET sock; char buf[2048]; enum message_concat_state state; std::string message_concat; struct addrinfo *res; }; class ftp_data_conn { public: ftp_data_conn(); explicit ftp_data_conn(CSTRING); ~ftp_data_conn(); STRING full_path, path; intmax_t filesz; std::vector vec; bool connect_passive(void); void get_file(void); SOCKET get_sock(void) const; numstr_t list_fetch(const int); void list_print(void); void send_file(void); private: FILE *fileptr; SOCKET sock; STRING host_str, port_str; char buf[4096]; enum message_concat_state state; std::string message_concat; struct addrinfo *res; uint16_t port; uint8_t h[4], p[2]; }; namespace ftp { extern ftp_ctl_conn *ctl_conn; extern ftp_data_conn *data_conn; extern volatile bool cmd_in_progress; extern volatile bool loop_get_file; extern volatile bool loop_send_file; void do_cmd_detached(CSTRING); NORETURN void exit_thread(void); void get_file(void); CSTRING get_upload_dir(void); void login(void); void ls_dir(void); bool passive(void); int send_bytes(SOCKET, const void *, const int); void send_file(void); int send_printf(SOCKET, CSTRING, ...) PRINTFLIKE(2); #if defined(UNIX) void set_timeout(SOCKET, int, const time_t); #elif defined(WIN32) void set_timeout(SOCKET, int, const DWORD); #endif void shutdown_sock(SOCKET); bool want_unveil_uploads(void); } #endif #endif swirc-3.5.5/src/commands/ignore.cpp000066400000000000000000000130001501213070300172140ustar00rootroot00000000000000/* Ignore commands Copyright (C) 2021-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include #include "../errHand.h" #include "../libUtils.h" #include "../printtext.h" #include "../strHand.h" #include "ignore.h" class ignore { std::string str; std::regex regex; public: explicit ignore(const char *); const std::string & get_str(void) const { return this->str; } const std::regex & get_regex(void) const { return this->regex; } }; ignore::ignore(const char *_str) { this->str.assign(_str); this->regex.assign(_str, std::regex::basic); } static const size_t regex_maxlen = 60; static std::vector ignore_list; static void print_ignore_list() { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1, true); printtext(&ctx, "List of ignores:"); int no = 0; auto it = ignore_list.begin(); while (it != ignore_list.end()) { printtext(&ctx, "%d: %s", no, it->get_str().c_str()); ++no; ++it; } } /* * usage: /ignore [regex] */ void cmd_ignore(const char *data) { PRINTTEXT_CONTEXT ctx; char *err_reason = nullptr; if (strings_match(data, "")) { print_ignore_list(); return; } else if (!is_valid_regex(data, &err_reason)) { print_and_free(err_reason, err_reason); return; } else if (ignore_list.size() >= MAXIGNORES) { printtext_print("err", "too many ignores!"); return; } ignore object(data); ignore_list.push_back(object); printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_SUCCESS, true); printtext(&ctx, "Added \"%s\" to ignore list.", object.get_str().c_str()); } /* * usage: /unignore [#] */ void cmd_unignore(const char *data) { PRINTTEXT_CONTEXT ctx; char *regex = nullptr; if (strings_match(data, "")) { print_ignore_list(); return; } try { char *ep = const_cast(""); long int no; if (ignore_list.empty()) throw std::runtime_error("ignore list empty"); errno = 0; no = strtol(data, &ep, 10); if (data[0] == '\0' || *ep != '\0') throw std::runtime_error("not a number"); else if (errno == ERANGE && (no == LONG_MAX || no == LONG_MIN)) throw std::runtime_error("out of range"); else if (no < 0 || static_cast(no) >= ignore_list.size() || no > MAXIGNORES) throw std::runtime_error("out of range"); regex = sw_strdup(ignore_list.at(no).get_str().c_str()); ignore_list.erase(ignore_list.begin() + no); printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_SUCCESS, true); printtext(&ctx, "Deleted \"%s\" from ignore list.", regex); free(regex); regex = nullptr; print_ignore_list(); return; } catch (const std::out_of_range &e) { printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "/unignore: %s", e.what()); } catch (const std::runtime_error &e) { printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "/unignore: %s", e.what()); } catch (...) { err_log(0, "/unignore: %s", "unknown exception!"); } free(regex); } bool is_in_ignore_list(const char *nick, const char *user, const char *host) { if (nick == nullptr || user == nullptr || host == nullptr || ignore_list.empty()) return false; std::string nuh(nick); nuh.append("!"); nuh.append(user).append("@").append(host); for (auto it = ignore_list.begin(); it != ignore_list.end(); ++it) { if (std::regex_match(nuh, it->get_regex())) { debug("is_in_ignore_list: \"%s\" matches \"%s\": " "returning true...", it->get_str().c_str(), nuh.c_str()); return true; } } return false; } bool is_valid_regex(const char *str, char **err_reason) { if (strings_match(str, "")) { *err_reason = sw_strdup("no regex"); return false; } else if (xstrnlen(str, (regex_maxlen + 1)) > regex_maxlen) { *err_reason = sw_strdup("regex too long"); return false; } try { std::regex regex(str, std::regex::basic); } catch (const std::regex_error &e) { *err_reason = sw_strdup(e.what()); return false; } *err_reason = nullptr; return true; } swirc-3.5.5/src/commands/ignore.h000066400000000000000000000004701501213070300166700ustar00rootroot00000000000000#ifndef CMD_IGNORE_H #define CMD_IGNORE_H #define MAXIGNORES 101 __SWIRC_BEGIN_DECLS void cmd_ignore(const char *) NONNULL; void cmd_unignore(const char *) NONNULL; bool is_in_ignore_list(const char *, const char *, const char *); bool is_valid_regex(const char *, char **) NONNULL; __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/info.cpp000066400000000000000000000053011501213070300166710ustar00rootroot00000000000000/* Informational commands Copyright (C) 2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "info.h" /* * usage: /admin [target] */ void cmd_admin(CSTRING data) { if (strings_match(data, "")) { if (net_send("ADMIN") < 0) printtext_print("err", "cannot send"); } else { if (net_send("ADMIN %s", data) < 0) printtext_print("err", "cannot send"); } } /* * usage: /info [target] */ void cmd_info(CSTRING data) { if (strings_match(data, "")) { if (net_send("INFO") < 0) printtext_print("err", "cannot send"); } else { if (net_send("INFO %s", data) < 0) printtext_print("err", "cannot send"); } } /* * usage: /ison [nick2] [nick3] [...] */ void cmd_ison(CSTRING data) { if (strings_match(data, "")) printtext_print("err", "missing args"); else if (net_send("ISON %s", data) < 0) printtext_print("err", "cannot send"); else return; } /* * usage: /servstats [ []] */ void cmd_servstats(CSTRING data) { if (strings_match(data, "")) { if (net_send("STATS") < 0) printtext_print("err", "cannot send"); } else { if (net_send("STATS %s", data) < 0) printtext_print("err", "cannot send"); } } swirc-3.5.5/src/commands/info.h000066400000000000000000000003011501213070300163310ustar00rootroot00000000000000#ifndef CMDS_INFO_H #define CMDS_INFO_H __SWIRC_BEGIN_DECLS void cmd_admin(CSTRING); void cmd_info(CSTRING); void cmd_ison(CSTRING); void cmd_servstats(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/invite.c000066400000000000000000000054331501213070300167020ustar00rootroot00000000000000/* Invite command Copyright (C) 2016-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../errHand.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "invite.h" /* * usage: /invite */ void cmd_invite(const char *data) { char *dcopy = sw_strdup(data); char *state = ""; char *targ_nick, *channel; if (strings_match(dcopy, "") || strFeed(dcopy, 1) != 1 || (targ_nick = strtok_r(dcopy, "\n", &state)) == NULL || (channel = strtok_r(NULL, "\n", &state)) == NULL) { print_and_free("/invite: missing arguments", dcopy); return; } else if (!is_valid_nickname(targ_nick) || !is_irc_channel(channel)) { print_and_free("/invite: bogus nickname or channel", dcopy); return; } else if (window_by_label(channel) == NULL) { print_and_free("/invite: not on that channel", dcopy); return; } else if (net_send("INVITE %s %s", targ_nick, channel) > 0) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1, true); printtext(&ctx, "Inviting %s%s%c to %s%s%s%c%s", COLOR1, targ_nick, NORMAL, LEFT_BRKT, COLOR2, channel, NORMAL, RIGHT_BRKT); } else { err_log(ENOTCONN, "/invite"); (void) atomic_swap_bool(&g_connection_lost, true); } free(dcopy); } swirc-3.5.5/src/commands/invite.h000066400000000000000000000001701501213070300167000ustar00rootroot00000000000000#ifndef CMD_INVITE_H #define CMD_INVITE_H __SWIRC_BEGIN_DECLS void cmd_invite(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/jp.cpp000066400000000000000000000106071501213070300163540ustar00rootroot00000000000000/* Command join and part Copyright (C) 2016-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "../config.h" #include "../dataClassify.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "jp.h" /* * usage: /join [key] */ void cmd_join(const char *data) { char *dcopy = sw_strdup(data); try { char *channel; char *key; char *state = const_cast(""); if (strings_match(dcopy, "") || (channel = strtok_r(dcopy, " ", &state)) == NULL) throw std::runtime_error("missing arguments"); const bool has_channel_key = (key = strtok_r(NULL, " ", &state)) != NULL; if (strtok_r(NULL, " ", &state) != NULL) throw std::runtime_error("implicit trailing data"); else if (strpbrk(channel, g_forbidden_chan_name_chars) != NULL) throw std::runtime_error("bogus irc channel"); else if (has_channel_key && strchr(key, ',') != NULL) throw std::runtime_error("commas aren't allowed in a key"); (void) strToLower(channel); std::string str(channel); if (!is_irc_channel(str.c_str())) (void) str.insert(0, "#"); if (has_channel_key) { if (net_send("JOIN %s %s", str.c_str(), key) < 0) throw std::runtime_error("cannot send"); } else { if (net_send("JOIN %s", str.c_str()) < 0) throw std::runtime_error("cannot send"); } } catch (const std::runtime_error &e) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "/join: %s", e.what()); } free(dcopy); } /* * usage: /part [channel] [message] */ void cmd_part(const char *data) { char *dcopy = sw_strdup(data); try { char *channel; char *message; char *state = const_cast(""); (void) strFeed(dcopy, 1); if (strings_match(dcopy, "") || (channel = strtok_r(dcopy, "\n", &state)) == NULL) { if (is_irc_channel(ACTWINLABEL)) { if (net_send("PART %s :%s", ACTWINLABEL, Config("part_message")) < 0) throw std::runtime_error("cannot send"); } else { throw std::runtime_error("missing arguments"); } free(dcopy); return; } const bool has_message = (message = strtok_r(NULL, "\n", &state)) != NULL; if (strtok_r(NULL, "\n", &state) != NULL) { throw std::runtime_error("implicit trailing data"); } else if (!is_irc_channel(channel) || strpbrk(channel + 1, g_forbidden_chan_name_chars) != NULL) { throw std::runtime_error("bogus irc channel"); } else if (has_message) { if (net_send("PART %s :%s", strToLower(channel), message) < 0) throw std::runtime_error("cannot send"); } else { if (net_send("PART %s", strToLower(channel)) < 0) throw std::runtime_error("cannot send"); } } catch (const std::runtime_error &e) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "/part: %s", e.what()); } free(dcopy); } swirc-3.5.5/src/commands/jp.h000066400000000000000000000002331501213070300160130ustar00rootroot00000000000000#ifndef CMDS_JOIN_PART_H #define CMDS_JOIN_PART_H __SWIRC_BEGIN_DECLS void cmd_join(const char *); void cmd_part(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/kick.c000066400000000000000000000071101501213070300163170ustar00rootroot00000000000000/* Command kick and kickban Copyright (C) 2016-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../errHand.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "kick.h" /* * usage: /kick [reason] */ void cmd_kick(const char *data) { char *dcopy = sw_strdup(data); char *nicks, *reason; char *state = ""; (void) strFeed(dcopy, 1); if (strings_match(dcopy, "") || (nicks = strtok_r(dcopy, "\n", &state)) == NULL) { print_and_free("/kick: missing arguments", dcopy); return; } const bool has_reason = (reason = strtok_r(NULL, "\n", &state)) != NULL; if (!is_irc_channel(ACTWINLABEL)) { print_and_free("/kick: active window isn't an irc channel", dcopy); return; } if (net_send("KICK %s %s :%s", ACTWINLABEL, nicks, (has_reason ? reason : "")) < 0) { err_log(ENOTCONN, "/kick"); (void) atomic_swap_bool(&g_connection_lost, true); } free(dcopy); } /* * usage: /kickban [reason] */ void cmd_kickban(const char *data) { char *dcopy = sw_strdup(data); char *state = ""; const char *nick = NULL, *mask = NULL; const char *reason = NULL; if (strings_match(dcopy, "")) { print_and_free("/kickban: missing arguments", dcopy); return; } (void) strFeed(dcopy, 2); nick = strtok_r(dcopy, "\n", &state); mask = strtok_r(NULL, "\n", &state); const bool has_reason = (reason = strtok_r(NULL, "\n", &state)) != NULL; if (nick == NULL) { print_and_free("/kickban: no nickname", dcopy); return; } else if (!is_valid_nickname(nick)) { print_and_free("/kickban: invalid nickname", dcopy); return; } else if (mask == NULL) { print_and_free("/kickban: no mask", dcopy); return; } else if (!is_irc_channel(ACTWINLABEL)) { print_and_free("/kickban: active window isn't an irc channel", dcopy); return; } if (net_send("MODE %s +b %s", ACTWINLABEL, mask) < 0 || net_send("KICK %s %s :%s", ACTWINLABEL, nick, (has_reason ? reason : "")) < 0) { err_log(ENOTCONN, "/kickban"); (void) atomic_swap_bool(&g_connection_lost, true); } free(dcopy); } swirc-3.5.5/src/commands/kick.h000066400000000000000000000002221501213070300163210ustar00rootroot00000000000000#ifndef CMD_KICK_H #define CMD_KICK_H __SWIRC_BEGIN_DECLS void cmd_kick(const char *); void cmd_kickban(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/me.c000066400000000000000000000013431501213070300160010ustar00rootroot00000000000000#include "common.h" #include "../irc.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "me.h" /* * usage: /me */ void cmd_me(const char *data) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if (strings_match(data, "")) { printtext(&ctx, "/me: missing arguments"); } else if (strings_match_ignore_case(ACTWINLABEL, g_status_window_label)) { printtext(&ctx, "/me: cannot send to status window!"); } else if (net_send("PRIVMSG %s :\001ACTION %s\001", ACTWINLABEL, data) < 0) { printtext(&ctx, "/me: cannot send!"); } else { ctx.spec_type = TYPE_SPEC_NONE; printtext(&ctx, " - %s %s", g_my_nickname, data); } } swirc-3.5.5/src/commands/me.h000066400000000000000000000001541501213070300160050ustar00rootroot00000000000000#ifndef CMD_ME_H #define CMD_ME_H __SWIRC_BEGIN_DECLS void cmd_me(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/misc.c000066400000000000000000000270011501213070300163320ustar00rootroot00000000000000/* Miscellaneous commands Copyright (C) 2016-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #ifdef UNIX #include /* shutdown() */ #endif #include "../irc.h" #include "../events/welcome.h" #include "../config.h" #include "../dataClassify.h" #include "../errHand.h" #include "../icb.h" #include "../io-loop.h" #include "../libUtils.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../terminal.h" #include "connect.h" #include "ftp.h" #include "misc.h" static void output_error(const char *message) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "%s", message); } /* * usage: /away [reason] */ void cmd_away(const char *data) { chararray_t msg = "/away: cannot send"; const bool has_reason = !strings_match(data, ""); if (has_reason) { if (net_send("AWAY :%s", data) < 0) output_error(msg); else g_is_away = true; } else { if (net_send("AWAY") < 0) output_error(msg); else g_is_away = false; } } /* * usage: /banlist [channel] */ void cmd_banlist(const char *data) { if (strings_match(data, "")) { if (is_irc_channel(g_active_window->label)) { if (net_send("MODE %s +b", g_active_window->label) < 0) output_error("/banlist: cannot send"); } else { output_error("/banlist: missing arguments"); } } else if (!is_irc_channel(data)) { output_error("/banlist: bogus irc channel"); } else { if (net_send("MODE %s +b", data) < 0) output_error("/banlist: cannot send"); } } /* * usage: /beep */ void cmd_beep(const char *data) { if (!g_icb_mode) return; if (!strings_match(data, "")) icb_send_beep(data); } /* * usage: /boot */ void cmd_boot(const char *data) { if (!g_icb_mode) return; if (!strings_match(data, "")) icb_send_boot(data); } /* * usage: /close */ void cmd_close(const char *data) { if (!strings_match(data, "")) output_error("/close: implicit trailing data"); else if (g_active_window == g_status_window) output_error("/close: cannot close status window"); else if (is_irc_channel(g_active_window->label) && atomic_load_bool(&g_on_air)) output_error("/close: cannot close window (connected)"); else (void) destroy_chat_window(g_active_window->label); } static bool has_channel_key(const char *channel, char **key) { PIRC_WINDOW win; char *chanmodes_copy = NULL; char *last = ""; char *modes; char *params[4] = { NULL }; size_t assigned_params = 0; if ((win = window_by_label(channel)) == NULL) goto no; chanmodes_copy = sw_strdup(win->chanmodes); if ((modes = strtok_r(chanmodes_copy, " ", &last)) == NULL || strchr(modes, 'k') == NULL) goto no; /* * We only want the modes that takes a value. For example 'l' * (user limit), and of course 'k' (channel key). The rest we * squeeze... */ squeeze(modes, "ABCDEFGHIJKLMNOPQRSTUVWXYZ+abcde" "ghi" "mnopqrstuvwxyz"); while (assigned_params < ARRAY_SIZE(params)) { char *token; if ((token = strtok_r(NULL, " ", &last)) == NULL) break; params[assigned_params] = token; assigned_params++; } const size_t spanned = strcspn(modes, "k"); if (assigned_params != strlen(modes) || spanned >= ARRAY_SIZE(params) || params[spanned] == NULL) goto no; *key = sw_strdup(params[spanned]); free(chanmodes_copy); return true; no: *key = NULL; free(chanmodes_copy); return false; } static void do_part_and_join(const char *_channel) { char *channel = sw_strdup(_channel); char *key = NULL; if (has_channel_key(channel, &key)) { (void) net_send("PART %s", channel); (void) net_send("JOIN %s %s", channel, key); } else { (void) net_send("PART %s", channel); (void) net_send("JOIN %s", channel); } free(channel); free(key); } /* * usage: /cycle [channel] */ void cmd_cycle(const char *data) { if (strings_match(data, "")) { if (is_irc_channel(g_active_window->label)) { do_part_and_join(g_active_window->label); } else { output_error("/cycle: missing arguments"); } } else if (!is_irc_channel(data)) { output_error("/cycle: bogus irc channel"); } else { do_part_and_join(data); } } /* * usage: /exlist [channel] */ void cmd_exlist(const char *data) { if (strings_match(data, "")) { if (is_irc_channel(g_active_window->label)) { if (net_send("MODE %s +e", g_active_window->label) < 0) output_error("/exlist: cannot send"); } else { output_error("/exlist: missing arguments"); } } else if (!is_irc_channel(data)) { output_error("/exlist: bogus irc channel"); } else { if (net_send("MODE %s +e", data) < 0) output_error("/exlist: cannot send"); } } /* * usage: /group */ void cmd_group(const char *data) { if (!g_icb_mode) return; if (!strings_match(data, "")) icb_send_group(data); } /* * usage: /ilist [channel] */ void cmd_ilist(const char *data) { if (strings_match(data, "")) { if (is_irc_channel(g_active_window->label)) { if (net_send("MODE %s +I", g_active_window->label) < 0) output_error("/ilist: cannot send"); } else { output_error("/ilist: missing arguments"); } } else if (!is_irc_channel(data)) { output_error("/ilist: bogus irc channel"); } else { if (net_send("MODE %s +I", data) < 0) output_error("/ilist: cannot send"); } } /* * usage: /kill */ void cmd_kill(const char *data) { if (strings_match(data, "")) output_error("/kill: missing arguments"); else (void) net_send("KILL %s", data); } /* * usage: /list [min_users][,pattern][...]] */ void cmd_list(const char *data) { const bool has_params = !strings_match(data, ""); if (g_icb_mode) { icb_send_who("-g"); return; } if (has_params) { if (net_send("LIST %s", data) < 0) { err_log(ENOTCONN, "/list"); (void) atomic_swap_bool(&g_connection_lost, true); } } else { if (net_send("LIST") < 0) { err_log(ENOTCONN, "/list"); (void) atomic_swap_bool(&g_connection_lost, true); } } } /* * usage: /mode [...] */ void cmd_mode(const char *data) { if (strings_match(data, "")) output_error("/mode: missing arguments"); else if (net_send("MODE %s", data) < 0) (void) atomic_swap_bool(&g_connection_lost, true); } /* * usage: /oper */ void cmd_oper(const char *data) { if (strings_match(data, "")) output_error("/oper: missing arguments"); else if (net_send("OPER %s", data) < 0) (void) atomic_swap_bool(&g_connection_lost, true); } /* * usage: /passmod */ void cmd_passmod(const char *data) { if (!g_icb_mode) return; if (!strings_match(data, "")) icb_send_pass_mod(data); } /* * usage: /query [nick] */ void cmd_query(const char *data) { if (strings_match(data, "")) { if (is_valid_nickname(g_active_window->label)) { switch (destroy_chat_window(g_active_window->label)) { case EINVAL: err_exit(EINVAL, "destroy_chat_window"); /* NOTREACHED */ break; case ENOENT: output_error("/query: cannot find window!"); break; default: break; } } else { output_error("/query: missing arguments"); } } else if (!is_valid_nickname(data)) { output_error("/query: bogus nickname"); } else { /* -------------------------------------------------- */ switch (spawn_chat_window(data, make_window_title(data))) { case EINVAL: err_exit(EINVAL, "spawn_chat_window"); /* NOTREACHED */ break; case ENOSPC: output_error("/query: too many windows open!"); break; default: break; } /* -------------------------------------------------- */ } } /* * usage: /quit [message] */ void cmd_quit(const char *data) { const bool has_message = !strings_match(data, ""); cmd_ftp("exit"); if (atomic_load_bool(&g_on_air)) { net_request_disconnect(); if (g_icb_mode) /* empty */; else if (has_message) (void) net_send("QUIT :%s", data); else (void) net_send("QUIT :%s", Config("quit_message")); if (atomic_load_bool(&g_connection_in_progress)) event_welcome_signalit(); while (atomic_load_bool(&g_irc_listening)) (void) napms(1); } g_io_loop = false; } /* * usage: /resize */ void cmd_resize(const char *data) { if (!strings_match(data, "")) output_error("/resize: implicit trailing data"); else term_resize_all(); } /* * usage: /rules */ void cmd_rules(const char *data) { if (!strings_match(data, "")) output_error("/rules: implicit trailing data"); else if (net_send("RULES") < 0) (void) atomic_swap_bool(&g_connection_lost, true); } void confirm_ctcp_sent(const char *cmd, const char *target) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_SUCCESS, true); printtext(&ctx, "CTCP %c%s%c request sent to %c%s%c", BOLD, cmd, BOLD, BOLD, target, BOLD); } /* * usage: /time */ void cmd_time(const char *data) { if (strings_match(data, "")) output_error("/time: missing arguments"); else if (!is_valid_nickname(data) && !is_irc_channel(data)) output_error("/time: neither a nickname or irc channel"); else if (net_send("PRIVMSG %s :\001TIME\001", data) > 0) confirm_ctcp_sent("TIME", data); else err_log(ENOTCONN, "/time"); } /* * usage: /version */ void cmd_version(const char *data) { if (strings_match(data, "")) output_error("/version: missing arguments"); else if (!is_valid_nickname(data) && !is_irc_channel(data)) output_error("/version: neither a nickname or irc channel"); else if (net_send("PRIVMSG %s :\001VERSION\001", data) > 0) confirm_ctcp_sent("VERSION", data); else err_log(ENOTCONN, "/version"); } /* * usage: /who */ void cmd_who(const char *data) { const bool has_mask = !strings_match(data, ""); if (has_mask) { if (net_send("WHO %s", data) < 0) (void) atomic_swap_bool(&g_connection_lost, true); } else { if (net_send("WHO") < 0) (void) atomic_swap_bool(&g_connection_lost, true); } } /* * usage: /whois */ void cmd_whois(const char *data) { if (strings_match(data, "")) output_error("/whois: missing arguments"); else if (!is_valid_nickname(data)) output_error("/whois: bogus nickname"); else if (net_send("WHOIS %s %s", data, data) < 0) (void) atomic_swap_bool(&g_connection_lost, true); } swirc-3.5.5/src/commands/misc.h000066400000000000000000000014751501213070300163460ustar00rootroot00000000000000#ifndef CMDS_MISC_H #define CMDS_MISC_H __SWIRC_BEGIN_DECLS void confirm_ctcp_sent(const char *cmd, const char *target); void cmd_away (const char *); void cmd_banlist (const char *); void cmd_beep (const char *); void cmd_boot (const char *); void cmd_close (const char *); void cmd_cycle (const char *); void cmd_exlist (const char *); void cmd_group (const char *); void cmd_ilist (const char *); void cmd_kill (const char *); void cmd_list (const char *); void cmd_mode (const char *); void cmd_oper (const char *); void cmd_passmod (const char *); void cmd_query (const char *); void cmd_quit (const char *); void cmd_resize (const char *); void cmd_rules (const char *); void cmd_time (const char *); void cmd_version (const char *); void cmd_who (const char *); void cmd_whois (const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/msg.c000066400000000000000000000061001501213070300161620ustar00rootroot00000000000000/* Message command Copyright (C) 2016-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../io-loop.h" #include "../printtext.h" #include "../strHand.h" #include "msg.h" /* * usage: /msg */ void cmd_msg(const char *data) { char *dcopy = sw_strdup(data); char *state = ""; const char *recipient, *message; if (strings_match(dcopy, "") || strFeed(dcopy, 1) != 1 || (recipient = strtok_r(dcopy, "\n", &state)) == NULL || (message = strtok_r(NULL, "\n", &state)) == NULL) { print_and_free("/msg: missing arguments", dcopy); return; } else if (!is_valid_nickname(recipient) && !is_irc_channel(recipient)) { print_and_free("/msg: neither a nickname or irc channel", dcopy); return; } else if (strings_match_ignore_case(recipient, "ChanServ")) { print_and_free("/msg: for safety reasons: " "consider using command /chanserv", dcopy); return; } else if (strings_match_ignore_case(recipient, "NickServ")) { print_and_free("/msg: for safety reasons: " "consider using command /nickserv", dcopy); return; } else if (window_by_label(recipient) == NULL && is_valid_nickname(recipient)) { if (spawn_chat_window(recipient, recipient) != 0) { print_and_free("/msg: fatal: cannot spawn chat window!", dcopy); return; } transmit_user_input(recipient, message); free(dcopy); } else if (window_by_label(recipient) == NULL && is_irc_channel(recipient)) { print_and_free("/msg: not on that channel", dcopy); return; } else { transmit_user_input(recipient, message); free(dcopy); } } swirc-3.5.5/src/commands/msg.h000066400000000000000000000001571501213070300161750ustar00rootroot00000000000000#ifndef CMD_MSG_H #define CMD_MSG_H __SWIRC_BEGIN_DECLS void cmd_msg(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/nick.c000066400000000000000000000013721501213070300163260ustar00rootroot00000000000000#include "common.h" #include "../dataClassify.h" #include "../icb.h" #include "../irc.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "nick.h" /* * usage: /nick */ void cmd_nick(const char *data) { PRINTTEXT_CONTEXT ctx; static const char cmd[] = "/nick"; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if (strings_match(data, "")) printtext(&ctx, "%s: missing arguments", cmd); else if (!is_valid_nickname(data)) printtext(&ctx, "%s: bogus nickname", cmd); else if (strings_match_ignore_case(g_my_nickname, data)) printtext(&ctx, "%s: no change", cmd); else if (g_icb_mode) icb_send_name(data); else (void) net_send("NICK %s", data); } swirc-3.5.5/src/commands/nick.h000066400000000000000000000001201501213070300163210ustar00rootroot00000000000000#ifndef CMD_NICK_H #define CMD_NICK_H void cmd_nick(const char *data); #endif swirc-3.5.5/src/commands/notice.cpp000066400000000000000000000061541501213070300172260ustar00rootroot00000000000000/* Notice command Copyright (C) 2016-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "../dataClassify.h" #include "../errHand.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "notice.h" #define B1 Theme("notice_inner_b1") #define B2 Theme("notice_inner_b2") /* * usage: /notice */ void cmd_notice(const char *data) { char *dcopy = sw_strdup(data); char *recipient, *message; char *state = const_cast(""); if (strings_match(dcopy, "") || strFeed(dcopy, 1) != 1 || (recipient = strtok_r(dcopy, "\n", &state)) == NULL || (message = strtok_r(NULL, "\n", &state)) == NULL) { print_and_free("/notice: missing arguments", dcopy); return; } else if (!is_valid_nickname(recipient) && !is_irc_channel(recipient)) { print_and_free("/notice: neither a nickname or irc channel", dcopy); return; } else if (window_by_label(recipient) == NULL && is_irc_channel(recipient)) { print_and_free("/notice: not on that channel", dcopy); return; } else if (net_send("NOTICE %s :%s", recipient, message) > 0) { PRINTTEXT_CONTEXT ctx; std::string str(LEFT_BRKT); (void) str.append(COLOR1).append("notice").append(TXT_NORMAL); (void) str.append(B1); (void) str.append(COLOR2).append(recipient).append(TXT_NORMAL); (void) str.append(B2); (void) str.append(RIGHT_BRKT); printtext_context_init(&ctx, g_active_window, TYPE_SPEC_NONE, true); printtext(&ctx, "%s %s", str.c_str(), message); } else { err_log(ENOTCONN, "/notice"); (void) atomic_swap_bool(&g_connection_lost, true); } free(dcopy); } swirc-3.5.5/src/commands/notice.h000066400000000000000000000001701501213070300166630ustar00rootroot00000000000000#ifndef CMD_NOTICE_H #define CMD_NOTICE_H __SWIRC_BEGIN_DECLS void cmd_notice(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/op.c000066400000000000000000000053261501213070300160230ustar00rootroot00000000000000/* Command op and deop Copyright (C) 2019-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "op.h" static const char err1[] = "missing arguments"; static const char err2[] = "invalid nickname"; static const char err3[] = "active window isn't an irc channel"; /* * usage: /op */ void cmd_op(const char *data) { static const char cmd[] = "/op"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_valid_nickname(data)) { printtext_print("err", "%s: %s", cmd, err2); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err3); return; } (void) net_send("MODE %s +o %s", ACTWINLABEL, data); } /* * usage: /deop */ void cmd_deop(const char *data) { static const char cmd[] = "/deop"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_valid_nickname(data)) { printtext_print("err", "%s: %s", cmd, err2); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err3); return; } (void) net_send("MODE %s -o %s", ACTWINLABEL, data); } swirc-3.5.5/src/commands/op.h000066400000000000000000000002111501213070300160140ustar00rootroot00000000000000#ifndef CMD_OP_H #define CMD_OP_H __SWIRC_BEGIN_DECLS void cmd_op(const char *); void cmd_deop(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/sasl-scram-sha.cpp000066400000000000000000000353021501213070300205600ustar00rootroot00000000000000/* SASL auth mechanism SCRAM-SHA-256 Copyright (C) 2019-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "base64.h" #include "common.h" #ifndef BSD #include #endif #include #include #include #include #include "../assertAPI.h" #include "../config.h" #include "../dataClassify.h" #include "../errHand.h" #include "../libUtils.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../strdup_printf.h" #include "sasl-scram-sha.h" struct digest_context { UCHARPTR key; int key_len; const unsigned char *data; size_t data_len; unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len; digest_context(); digest_context(UCHARPTR, int, const unsigned char *, size_t); }; digest_context::digest_context() : key(NULL) , key_len(0) , data(NULL) , data_len(0) , md_len(0) { BZERO(this->md, sizeof(this->md)); } digest_context::digest_context(UCHARPTR p_key, int p_key_len, const unsigned char *p_data, size_t p_data_len) : key(p_key) , key_len(p_key_len) , data(p_data) , data_len(p_data_len) , md_len(0) { BZERO(this->md, sizeof(this->md)); } volatile bool g_sasl_scram_sha_got_first_msg = false; static STRING complete_nonce = NULL; static char nonce[64] = { '\0' }; static const unsigned char client_key_str[] = "Client Key"; static const unsigned char server_key_str[] = "Server Key"; static unsigned char signature_expected[EVP_MAX_MD_SIZE] = { '\0' }; static unsigned int signature_expected_len = 0; /*lint -sem(get_decoded_msg, r_null) */ /*lint -sem(get_salted_password, r_null) */ static void generate_and_store_nonce(void) { static const char legal_index[] = "!\"#$%&'()*+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" "abcdefghijklmnopqrstuvwxyz{|}~"; #ifdef BSD for (size_t i = 0; i < ARRAY_SIZE(nonce); i++) { nonce[i] = legal_index[arc4random_uniform (sizeof legal_index - 1)]; } #else std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution dist(0, strlen(legal_index) - 1); for (size_t i = 0; i < ARRAY_SIZE(nonce); i++) nonce[i] = legal_index[dist(gen)]; #endif nonce[ARRAY_SIZE(nonce) - 1] = '\0'; debug("%s: nonce: %s", __func__, nonce); } static CSTRING get_encoded_msg(CSTRING source) { static char encoded_msg[4096] = { '\0' }; if (b64_encode(reinterpret_cast(source), strlen(source), encoded_msg, ARRAY_SIZE(encoded_msg)) == -1) return ""; return (&encoded_msg[0]); } /* C: n,,n=user,r=rOprNGfwEbeRWgbNEkqO */ int sasl_scram_sha_send_client_first_msg(void) { CSTRING encoded_msg; CSTRING username = Config("sasl_username"); STRING msg; if (!is_valid_username(username)) return -1; generate_and_store_nonce(); msg = strdup_printf("n,,n=%s,r=%s", username, nonce); encoded_msg = get_encoded_msg(msg); free(msg); if (strings_match(encoded_msg, "") || net_send("AUTHENTICATE %s", encoded_msg) < 0) return -1; return 0; } /* C: c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0, p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ= */ int sasl_scram_sha_send_client_final_msg(CSTRING proof) { STRING cli_final_msg; size_t size; std::string str("c=biws,r="); if (complete_nonce == NULL || proof == NULL) return -1; (void) str.append(complete_nonce); (void) str.append(",p=").append(proof); size = str.length() + 1; cli_final_msg = new char[size]; if (sw_strcpy(cli_final_msg, str.c_str(), size) != 0 || net_send("AUTHENTICATE %s", get_encoded_msg(cli_final_msg)) < 0) { delete[] cli_final_msg; return -1; } debug("%s: C: %s", __func__, cli_final_msg); delete[] cli_final_msg; return 0; } static STRING get_decoded_msg(CSTRING source, int *outlen) { STRING decoded_msg = NULL; int length_needed, tmp; if (source == NULL || strings_match(source, "") || (tmp = b64_decode(source, NULL, 0)) < 0) return NULL; if (outlen) *outlen = tmp; length_needed = int_sum(tmp, 1); decoded_msg = new char[length_needed]; decoded_msg[length_needed - 1] = '\0'; if (b64_decode(source, reinterpret_cast(decoded_msg), length_needed) == -1) { delete[] decoded_msg; return NULL; } return decoded_msg; } /* S: r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0, s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096 */ static int get_sfm_components(CSTRING msg, unsigned char **salt, int *saltlen, int *iter) { STRING decoded_msg = NULL; bool ok = false; *salt = NULL; *saltlen = 0; *iter = PKCS5_DEFAULT_ITER; try { char *cp, *b64salt; size_t n; if ((decoded_msg = get_decoded_msg(msg, NULL)) == NULL) { throw std::runtime_error("unable to get decoded " "message"); } debug("%s: S: %s", __func__, decoded_msg); cp = decoded_msg; if (strncmp(cp, "r=", 2) != STRINGS_MATCH) throw std::runtime_error("expected nonce"); cp += 2; if (strncmp(cp, nonce, strlen(nonce)) != STRINGS_MATCH) throw std::runtime_error("nonce mismatch"); n = strcspn(cp, ","); cp[n] = '\0'; free(complete_nonce); complete_nonce = sw_strdup(cp); cp[n] = ','; if ((cp = strstr(cp, ",s=")) == NULL) throw std::runtime_error("no base64-encoded salt"); cp += 3; n = strcspn(cp, ","); cp[n] = '\0'; b64salt = sw_strdup(cp); cp[n] = ','; *salt = reinterpret_cast(get_decoded_msg(b64salt, saltlen)); free(b64salt); if (*salt == NULL) throw std::runtime_error("unable to decode salt"); else if ((cp = strstr(cp, ",i=")) == NULL) throw std::runtime_error("no iteration count"); cp += 3; if (!is_numeric(cp)) throw std::runtime_error("iteration count not numeric"); *iter = static_cast(strtol(cp, NULL, 10)); ok = true; } catch (const std::runtime_error &e) { delete[] *salt; *salt = NULL; *saltlen = 0; *iter = PKCS5_DEFAULT_ITER; err_log(0, "%s: %s", __func__, e.what()); } delete[] decoded_msg; return (ok ? 0 : -1); } static const EVP_MD * get_evp(void) { CSTRING mech = Config("sasl_mechanism"); if (strings_match(mech, "SCRAM-SHA-1")) return EVP_sha1(); else if (strings_match(mech, "SCRAM-SHA-256")) return EVP_sha256(); else if (strings_match(mech, "SCRAM-SHA-512")) return EVP_sha512(); return EVP_sha256(); } /* * SaltedPassword: Hi(Normalize(password), salt, i) */ static UCHARPTR get_salted_password(const unsigned char *salt, int saltlen, int iter, int *outsize) { UCHARPTR out = NULL; try { CSTRING pass; if (*outsize = EVP_MD_size(get_evp()), *outsize < 0) { throw std::runtime_error("message digest size " "negative"); } out = new unsigned char[*outsize]; if ((pass = config_get_normalized_sasl_password()) == NULL) { throw std::runtime_error("unable to get normalized " "sasl password"); } else if (!PKCS5_PBKDF2_HMAC(pass, -1, salt, saltlen, iter, get_evp(), *outsize, out)) { throw std::runtime_error("unable to get salted " "password"); } } catch (const std::bad_alloc &e) { err_exit(ENOMEM, "%s: %s", __func__, e.what()); } catch (const std::runtime_error &e) { *outsize = 0; delete[] out; err_log(0, "%s: %s", __func__, e.what()); return NULL; } catch (...) { sw_assert_not_reached(); } return out; } static int get_digest(struct digest_context *ctx) { if (HMAC(get_evp(), ctx->key, ctx->key_len, ctx->data, ctx->data_len, ctx->md, & (ctx->md_len)) == NULL) return -1; return 0; } static STRING get_client_first_msg_bare() { STRING msg_bare; size_t size; std::string str("n="); (void) str.append(Config("sasl_username")); (void) str.append(",r=").append(nonce); size = str.length() + 1; msg_bare = new char[size]; errno = sw_strcpy(msg_bare, str.c_str(), size); sw_assert_perror(errno); return msg_bare; } static STRING get_client_final_msg_wo_proof() { STRING msg_wo_proof; size_t size; std::string str("c=biws,r="); (void) str.append(complete_nonce ? complete_nonce : ""); size = str.length() + 1; msg_wo_proof = new char[size]; errno = sw_strcpy(msg_wo_proof, str.c_str(), size); sw_assert_perror(errno); return msg_wo_proof; } /* * AuthMessage: client-first-message-bare + "," + * server-first-message + "," + * client-final-message-without-proof */ static UCHARPTR get_auth_msg(CSTRING b64msg, size_t *auth_msg_len) { STRING msg_bare, msg_wo_proof, serv_first_msg; STRING out; try { msg_bare = get_client_first_msg_bare(); msg_wo_proof = get_client_final_msg_wo_proof(); serv_first_msg = get_decoded_msg(b64msg, NULL); } catch (const std::bad_alloc &e) { err_exit(ENOMEM, "%s: fatal: %s", __func__, e.what()); } out = strdup_printf("%s,%s,%s", msg_bare, serv_first_msg, msg_wo_proof); delete[] msg_bare; delete[] msg_wo_proof; delete[] serv_first_msg; *auth_msg_len = strlen(out); return reinterpret_cast(out); } static UCHARPTR get_stored_key(int &digest_length, bool zero) { CSTRING mech = Config("sasl_mechanism"); static unsigned char key[SHA_DIGEST_LENGTH]; static unsigned char key256[SHA256_DIGEST_LENGTH]; static unsigned char key512[SHA512_DIGEST_LENGTH]; if (zero) { BZERO(key, sizeof key); BZERO(key256, sizeof key256); BZERO(key512, sizeof key512); } if (strings_match(mech, "SCRAM-SHA-1")) { digest_length = SHA_DIGEST_LENGTH; return addrof(key[0]); } else if (strings_match(mech, "SCRAM-SHA-256")) { digest_length = SHA256_DIGEST_LENGTH; return addrof(key256[0]); } else if (strings_match(mech, "SCRAM-SHA-512")) { digest_length = SHA512_DIGEST_LENGTH; return addrof(key512[0]); } digest_length = SHA256_DIGEST_LENGTH; return addrof(key256[0]); } static UCHARPTR hash_client_key(UCHARPTR md, unsigned int md_len, UCHARPTR stored_key) { CSTRING mech = Config("sasl_mechanism"); if (strings_match(mech, "SCRAM-SHA-1")) return SHA1(md, md_len, stored_key); else if (strings_match(mech, "SCRAM-SHA-256")) return SHA256(md, md_len, stored_key); else if (strings_match(mech, "SCRAM-SHA-512")) return SHA512(md, md_len, stored_key); return SHA256(md, md_len, stored_key); } int sasl_scram_sha_handle_serv_first_msg(CSTRING msg) { UCHARPTR auth_msg; UCHARPTR pass = NULL; UCHARPTR salt = NULL; char proof[EVP_MAX_MD_SIZE + 1] = { '\0' }; int digest_len = 0; int iter = PKCS5_DEFAULT_ITER; int passwdlen = 0; int saltlen = 0; size_t auth_msg_len; static UCHARPTR stored_key; // XXX if (get_sfm_components(msg, &salt, &saltlen, &iter) == -1 || (pass = get_salted_password(salt, saltlen, iter, &passwdlen)) == NULL) { delete[] salt; return -1; } delete[] salt; /*************************************************** * * ClientKey: HMAC(SaltedPassword, "Client Key") * ServerKey: HMAC(SaltedPassword, "Server Key") * */ struct digest_context client_key(pass, passwdlen, client_key_str, 10); struct digest_context server_key(pass, passwdlen, server_key_str, 10); if (get_digest(&client_key) == -1 || get_digest(&server_key) == -1) { delete[] pass; return -1; } delete[] pass; /* * StoredKey: H(ClientKey) */ if (hash_client_key(client_key.md, client_key.md_len, get_stored_key(digest_len, true)) == NULL) return -1; auth_msg_len = 0; auth_msg = get_auth_msg(msg, &auth_msg_len); /*************************************************** * * ClientSignature: HMAC(StoredKey, AuthMessage) * ServerSignature: HMAC(ServerKey, AuthMessage) * */ stored_key = get_stored_key(digest_len, false); struct digest_context client_signature(stored_key, digest_len, auth_msg, auth_msg_len); struct digest_context server_signature(server_key.md, server_key.md_len, auth_msg, auth_msg_len); if (get_digest(&client_signature) == -1 || get_digest(&server_signature) == -1) { free(auth_msg); return -1; } free(auth_msg); (void) memcpy(signature_expected, server_signature.md, server_signature.md_len); signature_expected_len = server_signature.md_len; if (client_key.md_len != client_signature.md_len) err_log(0, "%s: lengths differ", __func__); const unsigned int len = MIN(client_key.md_len, client_signature.md_len); if (len >= ARRAY_SIZE(proof)) { err_log(EOVERFLOW, "%s", __func__); return -1; } /* * ClientProof: ClientKey XOR ClientSignature */ for (unsigned int i = 0; i < len; i++) proof[i] = client_key.md[i] ^ client_signature.md[i]; return sasl_scram_sha_send_client_final_msg(get_encoded_msg(proof)); } /* * S: v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4= */ int sasl_scram_sha_handle_serv_final_msg(CSTRING msg) { STRING decoded_msg = NULL; bool signature_ok = false; try { char *cp; UCHARPTR signature; if ((decoded_msg = get_decoded_msg(msg, NULL)) == NULL) { throw std::runtime_error("unable to get decoded " "message"); } debug("%s: S: %s", __func__, decoded_msg); if (strncmp(decoded_msg, "v=", 2) != STRINGS_MATCH) throw std::runtime_error("expected server signature"); cp = decoded_msg; cp += 2; if ((signature = reinterpret_cast(get_decoded_msg(cp, NULL))) == NULL) { throw std::runtime_error("cannot decode server " "signature!"); } signature_ok = (memcmp(signature, signature_expected, signature_expected_len) == 0); delete[] signature; } catch (const std::runtime_error &e) { delete[] decoded_msg; err_log(0, "%s: %s", __func__, e.what()); return -1; } delete[] decoded_msg; free_and_null(&complete_nonce); return (signature_ok ? 0 : -1); } swirc-3.5.5/src/commands/sasl-scram-sha.h000066400000000000000000000005471501213070300202300ustar00rootroot00000000000000#ifndef SASL_SCRAM_SHA_H #define SASL_SCRAM_SHA_H __SWIRC_BEGIN_DECLS extern volatile bool g_sasl_scram_sha_got_first_msg; int sasl_scram_sha_send_client_first_msg(void); int sasl_scram_sha_send_client_final_msg(CSTRING proof); int sasl_scram_sha_handle_serv_first_msg(CSTRING); int sasl_scram_sha_handle_serv_final_msg(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/sasl.cpp000066400000000000000000000425551501213070300167140ustar00rootroot00000000000000/* commands/sasl.cpp Copyright (C) 2017-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "base64.h" #include "common.h" /* * Certain low-level functions from the OpenSSL toolkit that we use in * this file have been flagged deprecated in OpenSSL 3.0. They aren't * deprecated in LibreSSL. Some redesign of this file is certainly * needed in order to use the new API. But I haven't investigated it * closer yet nor if it's possible to accomplish the same effect using * the new higher level API. Suppress for now... */ #define OPENSSL_API_COMPAT 10101 #include #include #include #include #include "../irc.h" #include "../events/cap.h" #include "../config.h" #include "../crypt.h" #include "../errHand.h" #include "../filePred.h" #include "../get_x509_fp.hpp" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../printtext.h" #include "../strHand.h" #include "../strdup_printf.h" #include "sasl.h" #ifndef PATH_MAX #define PATH_MAX 1024 #endif const char g_decrypted_pass_sym = '%'; const char g_encrypted_pass_sym = '#'; const char g_unencrypted_pass_sym = '?'; const char g_sasl_pass_allowed_chars[100] = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" "abcdefghijklmnopqrstuvwxyz{|}~"; static const size_t password_min = 8; static const size_t password_max = 200; static stringarray_t sasl_cmds = { "keygen", "pubkey", "mechanism ", "mechanism ecdsa-nist256p-challenge", "mechanism external", "mechanism plain", "mechanism scram-sha-1", "mechanism scram-sha-256", "mechanism scram-sha-512", "username ", "password ", "passwd_s ", "set ", "set on", "set off", "x509 ", "x509 client.pem", }; /*lint -sem(get_filepath, r_null) */ static char * get_filepath(const bool is_public) { static char path[PATH_MAX] = { '\0' }; if (sw_strcpy(path, g_home_dir, sizeof path) != 0) return NULL; else if (sw_strcat(path, SLASH, sizeof path) != 0) return NULL; else if (sw_strcat(path, (is_public ? "ec_key.pub" : "ec_key"), sizeof path) != 0) return NULL; return (&path[0]); } static void output_message(const bool is_error, const char *message) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, (is_error ? TYPE_SPEC1_FAILURE : TYPE_SPEC1_SUCCESS), true); printtext(&ctx, "%s", message); } static void sasl_keygen(const bool force) { EC_KEY *key = NULL; FILE *fp = NULL; try { char *path; if ((path = get_filepath(false)) == NULL) { throw std::runtime_error("unable to get file path"); } else if (file_exists(path) && !force) { throw std::runtime_error("file exists! add --force to " "overwrite it"); } else if ((key = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1)) == NULL) { throw std::runtime_error("unable to construct ec key"); } else { EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED); } if (!EC_KEY_generate_key(key)) throw std::runtime_error("key generation failed"); else if ((fp = xfopen(path, "w")) == NULL) throw std::runtime_error("unable to open file"); else if (!PEM_write_ECPrivateKey(fp, key, NULL, NULL, 0, NULL, NULL)) throw std::runtime_error("unable to write private key"); output_message(false, "key generation successful"); } catch (const std::runtime_error &e) { output_message(true, e.what()); } catch (...) { output_message(true, "unknown exception was thrown!"); } if (key) EC_KEY_free(key); if (fp) (void) fclose(fp); } static int public_key_length(EC_KEY *key) { if (key == NULL) return -1; return (i2o_ECPublicKey(key, NULL)); } /*lint -sem(public_key_blob, r_null) */ static unsigned char * public_key_blob(EC_KEY *key) { int length; unsigned char *out, *out_p; if (key == NULL || (length = public_key_length(key)) <= 0) return NULL; out = static_cast(xmalloc(length)); out[length - 1] = '\0'; out_p = &out[0]; if (i2o_ECPublicKey(key, &out_p) < 0) { free(out); return NULL; } return out; } static void sasl_pubkey(void) { EC_KEY *key = NULL; FILE *fp = NULL; char *encoded_blob = NULL; unsigned char *blob = NULL; try { char *msg, *path; int encode_len, encode_ret; int length = -1; if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) throw std::runtime_error("unable to construct ec key"); else EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED); if ((path = get_filepath(false)) == NULL) { throw std::runtime_error("unable to get file path"); } else if (!file_exists(path)) { throw std::runtime_error("unable to find private key " "(doesn't exist -- use keygen)"); } else if ((fp = xfopen(path, "r")) == NULL) { throw std::runtime_error("unable to open private key"); } else if (PEM_read_ECPrivateKey(fp, &key, NULL, NULL) == NULL) { throw std::runtime_error("unable to read private key"); } else if (!EC_KEY_check_key(key)) { throw std::runtime_error("bogus key"); } else if ((length = public_key_length(key)) <= 0) { throw std::runtime_error("public key length error"); } else if ((blob = public_key_blob(key)) == NULL) { throw std::runtime_error("public key blob error"); } else if ((encode_len = crypt_get_base64_encode_length(length)) <= 0) { throw std::runtime_error("encode length error"); } encoded_blob = static_cast(xmalloc(encode_len)); encoded_blob[encode_len - 1] = '\0'; if ((encode_ret = b64_encode(blob, static_cast(length), encoded_blob, static_cast(encode_len))) == -1) throw std::runtime_error("encoding error"); UNUSED_VAR(encode_ret); msg = strdup_printf("public key is: %s", encoded_blob); output_message(false, msg); free(msg); } catch (const std::runtime_error &e) { output_message(true, e.what()); } catch (...) { output_message(true, "unknown exception was thrown!"); } if (key) EC_KEY_free(key); if (fp != NULL) (void) fclose(fp); free(encoded_blob); free(blob); } static bool modify_setting(const char *setting_name, const char *new_value) { if (config_item_undef(setting_name) != 0 || config_item_install(setting_name, new_value) != 0) return false; return true; } static void save_to_config(void) { char path[PATH_MAX] = { '\0' }; size_t len = 0; if (g_home_dir == NULL) return; len += strlen(g_home_dir); len += strlen(SLASH); len += strlen("swirc"); len += strlen(g_config_filesuffix); if (len >= sizeof path) { err_log(ENAMETOOLONG, "save_to_config"); return; } (void) sw_strcpy(path, g_home_dir, sizeof path); (void) sw_strcat(path, SLASH, sizeof path); (void) sw_strcat(path, "swirc", sizeof path); (void) sw_strcat(path, g_config_filesuffix, sizeof path); config_do_save(path, "w"); } static void set_mechanism(char *mechanism) { (void) strToUpper(mechanism); if (!is_sasl_mechanism_supported(mechanism)) { output_message(true, "sasl mechanism unknown"); return; } else if (!modify_setting("sasl_mechanism", mechanism)) { output_message(true, "set mechanism failed"); return; } else { output_message(false, "ok"); save_to_config(); } } static void set_username(const char *username) { if (!modify_setting("sasl_username", username)) { output_message(true, "set username failed"); return; } else { output_message(false, "set username ok"); save_to_config(); } } static void set_password(const char *password) { std::string str(""); str.push_back(g_unencrypted_pass_sym); str.append(password); if (!modify_setting("sasl_password", str.c_str())) { output_message(true, "set password failed"); return; } else { output_message(false, "set password ok"); save_to_config(); } } static bool passwd_ok_check(const char *str, std::string &err_reason) { if (str == NULL) { err_reason.assign("is null"); return false; } else if (strings_match(str, "")) { err_reason.assign("is empty"); return false; } for (const char *cp = str; *cp != '\0'; cp++) { if (strchr(g_sasl_pass_allowed_chars, *cp) == NULL) { err_reason.assign("contains forbidden chars"); return false; } } if (strlen(str) < password_min) { err_reason.assign("is too short"); return false; } else if (strlen(str) > password_max) { err_reason.assign("is too long"); return false; } err_reason.assign(""); return true; } static void set_passwd_s(const char *data) { bool error = false; char *cout = NULL; char *data_copy = NULL; char *val = NULL; size_t data_len = 0; try { char *last = const_cast(""); char *sasl_pass, *encryption_pass; cryptstr_t str1, str2; std::string err_reason(""); std::string msg(""); if (data == NULL || strings_match(data, "")) throw std::runtime_error("no data"); data_copy = sw_strdup(data); data_len = strlen(data); if ((sasl_pass = strtok_r(data_copy, " ", &last)) == NULL || (encryption_pass = strtok_r(NULL, " ", &last)) == NULL) { throw std::runtime_error("too few args"); } else if (strtok_r(NULL, " ", &last) != NULL) { throw std::runtime_error("too many args"); } else if (!passwd_ok_check(sasl_pass, err_reason)) { msg.assign("SASL pass: ").append(err_reason); throw std::runtime_error(msg.c_str()); } else if (!passwd_ok_check(encryption_pass, err_reason)) { msg.assign("Encryption pass: ").append(err_reason); throw std::runtime_error(msg.c_str()); } str1 = reinterpret_cast(sasl_pass); str2 = reinterpret_cast(encryption_pass); if ((cout = crypt_encrypt_str(str1, str2, true)) == NULL) throw std::runtime_error("encryption failed"); val = strdup_printf("%c%s", g_encrypted_pass_sym, cout); free_and_null(&cout); if (!modify_setting("sasl_password", val)) throw std::runtime_error("unable to modify setting"); save_to_config(); } catch (const std::runtime_error &e) { error = true; printtext_print("err", "%s", e.what()); } catch (...) { error = true; printtext_print("err", "%s", "unknown exception was thrown!"); } if (!error) printtext_print("success", "sasl_password = %s", Config("sasl_password")); crypt_freezero(data_copy, data_len); free(cout); free(val); } static void set_state(char *state) { (void) strToLower(state); if (!strings_match(state, "on") && !strings_match(state, "off")) { output_message(true, "what? on or off?"); return; } else if (!modify_setting("sasl", state)) { output_message(true, "set sasl on/off failed"); return; } else { char *msg; msg = strdup_printf("SASL authentication is now %s", (strings_match(state, "on") ? "ON" : "OFF")); output_message(false, msg); free(msg); save_to_config(); } } static void set_x509(const char *filename) { static const char err_prefix[] = "fingerprint: "; if (!modify_setting("sasl_x509", filename)) { output_message(true, "set x509 failed"); return; } output_message(false, "set x509 ok"); save_to_config(); try { std::string path(""); (void) path.append(g_home_dir); (void) path.append(SLASH); (void) path.append(Config("sasl_x509")); x509_fingerprint fp(path.c_str()); fp.show_fp(); } catch (const std::runtime_error &e) { printtext_print("err", "%s%s", err_prefix, e.what()); } catch (...) { printtext_print("err", "%s%s", err_prefix, "unknown exception was thrown!"); } } /* usage: /sasl [...] keygen [--force] pubkey mechanism [ecdsa-nist256p-challenge | external | plain | scram-sha-1,256,512] username password passwd_s set [on | off] x509 */ void cmd_sasl(const char *data) { char mechanism[31] = { '\0' }; char username[101] = { '\0' }; char password[301] = { '\0' }; char state[11] = { '\0' }; char filename[51] = { '\0' }; /* * sscanf() is safe in this context */ #if WIN32 #pragma warning(disable: 4996) #endif if (strings_match(data, "keygen") || strings_match(data, "KEYGEN")) sasl_keygen(false); else if (strings_match(data, "keygen --force") || strings_match(data, "KEYGEN --force")) sasl_keygen(true); else if (strings_match(data, "pubkey") || strings_match(data, "PUBKEY")) sasl_pubkey(); else if (sscanf(data, "mechanism %30s", mechanism) == 1) set_mechanism(mechanism); else if (sscanf(data, "username %100s", username) == 1) set_username(username); else if (sscanf(data, "password %300s", password) == 1) set_password(password); else if (!strncmp(data, "passwd_s ", 9)) set_passwd_s(addrof(data[9])); else if (sscanf(data, "set %10s", state) == 1) set_state(state); else if (sscanf(data, "x509 %50s", filename) == 1) set_x509(filename); else output_message(true, "bogus operation"); /* * Reset warning behavior to its default value */ #if WIN32 #pragma warning(default: 4996) #endif } static bool sign_decoded_data(EC_KEY *key, const uint8_t *data, int datalen, uint8_t **sig, unsigned int *siglen) { unsigned int len = 0; if (key == NULL || (len = static_cast(ECDSA_size(key))) == 0) { *sig = NULL; *siglen = 0; return false; } *sig = static_cast(xmalloc(len)); (*sig)[len - 1] = '\0'; if (!ECDSA_sign(0, data, datalen, *sig, &len, key)) { free(*sig); *sig = NULL; *siglen = 0; return false; } *siglen = len; return true; } PTEXTBUF get_list_of_matching_sasl_cmds(const char *search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(sasl_cmds); i++) { const char *cmd = sasl_cmds[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } static void clean_up(EC_KEY *key, FILE *fp, char *out, uint8_t *decoded_chl, uint8_t *sig) { if (key) EC_KEY_free(key); if (fp != NULL && fclose(fp) != 0) err_log(errno, "solve_ecdsa_nist256p_challenge: fclose"); free(out); free(decoded_chl); free(sig); } char * solve_ecdsa_nist256p_challenge(const char *challenge, char **err_reason) { EC_KEY *key = NULL; FILE *fp = NULL; char *out = NULL; uint8_t *decoded_chl = NULL; uint8_t *sig = NULL; try { char *path = NULL; int decode_len, encode_len; int decode_ret = -1; unsigned int siglen = 0; if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) throw std::runtime_error("unable to construct ec key"); else EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED); if ((path = get_filepath(false)) == NULL) throw std::runtime_error("unable to get file path"); else if (!file_exists(path)) throw std::runtime_error("unable to find private key"); else if ((fp = xfopen(path, "r")) == NULL) throw std::runtime_error("unable to open private key"); else if (PEM_read_ECPrivateKey(fp, &key, NULL, NULL) == NULL) throw std::runtime_error("unable to read private key"); else if (!EC_KEY_check_key(key)) throw std::runtime_error("bogus key"); else if ((decode_len = crypt_get_base64_decode_length(challenge)) <= 0) throw std::runtime_error("decode length error"); decoded_chl = static_cast(xmalloc(decode_len)); decoded_chl[decode_len - 1] = '\0'; if ((decode_ret = b64_decode(challenge, decoded_chl, static_cast (decode_len))) == -1) throw std::runtime_error("decoding error"); else if (!sign_decoded_data(key, decoded_chl, decode_ret, &sig, &siglen)) throw std::runtime_error("signing error"); else if ((encode_len = crypt_get_base64_encode_length (static_cast(siglen))) <= 0) throw std::runtime_error("encode length error"); out = static_cast(xmalloc(encode_len)); out[encode_len - 1] = '\0'; if (b64_encode(sig, siglen, out, static_cast (encode_len)) == -1) throw std::runtime_error("encoding error"); } catch (const std::runtime_error &e) { clean_up(key, fp, out, decoded_chl, sig); *err_reason = sw_strdup(e.what()); return NULL; } catch (...) { clean_up(key, fp, out, decoded_chl, sig); *err_reason = sw_strdup("unknown exception was thrown!"); return NULL; } clean_up(key, fp, NULL, decoded_chl, sig); *err_reason = NULL; return out; } swirc-3.5.5/src/commands/sasl.h000066400000000000000000000010431501213070300163440ustar00rootroot00000000000000#ifndef CMD_SASL_H #define CMD_SASL_H #include "../textBuffer.h" /*lint -sem(get_list_of_matching_sasl_cmds, r_null) */ /*lint -sem(solve_ecdsa_nist256p_challenge, r_null) */ __SWIRC_BEGIN_DECLS extern const char g_decrypted_pass_sym; extern const char g_encrypted_pass_sym; extern const char g_unencrypted_pass_sym; extern const char g_sasl_pass_allowed_chars[100]; void cmd_sasl(const char *); PTEXTBUF get_list_of_matching_sasl_cmds(const char *); char *solve_ecdsa_nist256p_challenge(const char *, char **); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/say.c000066400000000000000000000010261501213070300161720ustar00rootroot00000000000000#include "common.h" #include "../io-loop.h" #include "../printtext.h" #include "../strHand.h" #include "say.h" /* * usage: /say */ void cmd_say(const char *data) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if (strings_match(data, "")) printtext(&ctx, "/say: missing arguments"); else if (strings_match_ignore_case(ACTWINLABEL, g_status_window_label)) printtext(&ctx, "/say: cannot say to status window"); else transmit_user_input(ACTWINLABEL, data); } swirc-3.5.5/src/commands/say.h000066400000000000000000000001571501213070300162030ustar00rootroot00000000000000#ifndef CMD_SAY_H #define CMD_SAY_H __SWIRC_BEGIN_DECLS void cmd_say(const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/services.cpp000066400000000000000000000117061501213070300175670ustar00rootroot00000000000000/* Communicate with IRC services Copyright (C) 2016-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "../config.h" #include "../dataClassify.h" #include "../errHand.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "services.h" #include "squery.h" static stringarray_t cs_cmds = { //lint -e786 cs_cmds_macro(--), //lint +e786 }; static stringarray_t ns_cmds = { //lint -e786 ns_cmds_macro(--), //lint +e786 }; class irc_service_cmd { std::string srv_host; std::string msg; public: explicit irc_service_cmd(CSTRING); const std::string & get_srv_host(void) const { return this->srv_host; } CSTRING get_msg(void) const { return this->msg.c_str(); } }; irc_service_cmd::irc_service_cmd(CSTRING data) { STRING dcopy; STRING token1, token2; char *last = const_cast(""); if (strings_match(data, "")) throw std::runtime_error("no data"); dcopy = sw_strdup(data); (void) strFeed(dcopy, 1); if ((token1 = strtok_r(dcopy, "\n", &last)) != NULL) (void) this->srv_host.assign(token1); if ((token2 = strtok_r(NULL, "\n", &last)) != NULL) (void) this->msg.assign(token2); free(dcopy); if (token1 == NULL || token2 == NULL) throw std::runtime_error("too few tokens"); } static void run_command(CSTRING slashcmd, CSTRING srv_name, CSTRING host_setting, CSTRING data) { try { irc_service_cmd sc(data); if (sc.get_srv_host().compare("--") == 0) { if (!is_valid_hostname(Config(host_setting))) throw std::runtime_error("invalid host"); else if (net_send("PRIVMSG %s@%s :%s", srv_name, Config(host_setting), sc.get_msg()) < 0) throw std::runtime_error("cannot send"); } else { if (!is_valid_hostname(sc.get_srv_host().c_str())) throw std::runtime_error("invalid host"); else if (net_send("PRIVMSG %s@%s :%s", srv_name, sc.get_srv_host().c_str(), sc.get_msg()) < 0) throw std::runtime_error("cannot send"); } } catch (const std::runtime_error &e) { printtext_print("err", "%s: %s", slashcmd, e.what()); if (strings_match(e.what(), "cannot send")) { err_log(ENOTCONN, "%s", slashcmd); (void) atomic_swap_bool(&g_connection_lost, true); } } } /* * usage: /chanserv <[service hostname | --]> [...] */ void cmd_chanserv(CSTRING data) { run_command("/chanserv", "ChanServ", "chanserv_host", data); } /* * usage: /nickserv <[service hostname | --]> [...] */ void cmd_nickserv(CSTRING data) { run_command("/nickserv", "NickServ", "nickserv_host", data); } /* * usage: /qbot <[service hostname | --]> [...] */ void cmd_qbot(CSTRING data) { run_command("/qbot", "Q", "qbot_host", data); } static PTEXTBUF get_list(CSTRING search_var, stringarray_t array, const size_t size) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < size; i++) { CSTRING cmd = array[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } return matches; } PTEXTBUF get_list_of_matching_cs_cmds(CSTRING search_var) { PTEXTBUF matches = get_list(search_var, cs_cmds, ARRAY_SIZE(cs_cmds)); if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } PTEXTBUF get_list_of_matching_ns_cmds(CSTRING search_var) { PTEXTBUF matches = get_list(search_var, ns_cmds, ARRAY_SIZE(ns_cmds)); if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } swirc-3.5.5/src/commands/services.h000066400000000000000000000005621501213070300172320ustar00rootroot00000000000000#ifndef CMD_SERVICES_H #define CMD_SERVICES_H __SWIRC_BEGIN_DECLS void cmd_chanserv(CSTRING); void cmd_nickserv(CSTRING); void cmd_qbot(CSTRING); //lint -sem(get_list_of_matching_cs_cmds, r_null) //lint -sem(get_list_of_matching_ns_cmds, r_null) PTEXTBUF get_list_of_matching_cs_cmds(CSTRING); PTEXTBUF get_list_of_matching_ns_cmds(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/servlist.cpp000066400000000000000000000004331501213070300176120ustar00rootroot00000000000000#include "common.h" #include "../network.h" #include "../strHand.h" #include "servlist.h" /* * usage: /servlist [ []] */ void cmd_servlist(const char *data) { if (strings_match(data, "")) (void)net_send("SERVLIST"); else (void)net_send("SERVLIST %s", data); } swirc-3.5.5/src/commands/servlist.h000066400000000000000000000002061501213070300172550ustar00rootroot00000000000000#ifndef CMD_SERVLIST_H #define CMD_SERVLIST_H __SWIRC_BEGIN_DECLS void cmd_servlist(const char *) NONNULL; __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/squery.cpp000066400000000000000000000074111501213070300172720ustar00rootroot00000000000000/* commands/squery.cpp Copyright (C) 2020-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include #include "../errHand.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "squery.h" static stringarray_t squery_commands = { /* Alis v2.4 */ "alis ", "alis admin", "alis die", "alis hash", "alis info", "alis list ", "alis help", "alis help ", "alis help admin", "alis help die", "alis help hash", "alis help info", "alis help list", "alis help status", "alis help version", /* Clis v1.0a2 */ "clis ", "clis admin", "clis info", "clis list ", "clis list -min ", "clis list -max ", "clis list --topic ", "clis list --show ", "clis version", "clis help", "clis help ", "clis help admin", "clis help info", "clis help list", "clis help version", //lint -e786 cs_cmds_macro(chanserv), ns_cmds_macro(nickserv), //lint +e786 }; /* * usage: /squery */ void cmd_squery(CSTRING data) { STRING dcopy; static const char cmd[] = "/squery"; if (strings_match(data, "")) { printtext_print("err", "%s: missing arguments", cmd); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 1); std::istringstream input(dcopy); free(dcopy); try { std::string token; std::vector tokens; while (std::getline(input, token)) tokens.push_back(token); if (tokens.size() != 2) throw std::runtime_error("missing arguments"); CSTRING servicename = tokens.at(0).c_str(); CSTRING text = tokens.at(1).c_str(); if (net_send("SQUERY %s :%s", servicename, text) < 0) throw std::runtime_error("cannot send"); } catch (const std::runtime_error &e) { printtext_print("err", "%s: %s", cmd, e.what()); } catch (...) { printtext_print("err", "%s: %s", cmd, "unknown exception"); } } PTEXTBUF get_list_of_matching_squery_commands(CSTRING search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(squery_commands); i++) { CSTRING cmd = squery_commands[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } swirc-3.5.5/src/commands/squery.h000066400000000000000000000040201501213070300167300ustar00rootroot00000000000000#ifndef CMD_SQUERY_H #define CMD_SQUERY_H #include "../textBuffer.h" #define cs_cmds_macro(x)\ (#x " "),\ (#x " ban "),\ (#x " drop "),\ (#x " getkey "),\ (#x " info "),\ (#x " invite "),\ (#x " kick "),\ (#x " list "),\ (#x " register "),\ (#x " status "),\ (#x " unban ") #define ns_cmds_macro(x)\ (#x " "),\ (#x " access "),\ (#x " access add "),\ (#x " access del "),\ (#x " access list"),\ (#x " access list "),\ (#x " alist"),\ (#x " alist "),\ (#x " cert add "),\ (#x " cert del "),\ (#x " cert list"),\ (#x " cert list "),\ (#x " confirm "),\ (#x " drop "),\ (#x " glist"),\ (#x " group"),\ (#x " group "),\ (#x " identify "),\ (#x " info"),\ (#x " info "),\ (#x " list "),\ (#x " logout"),\ (#x " logout "),\ (#x " recover "),\ (#x " register "),\ (#x " resetpass "),\ (#x " set "),\ (#x " set autoop "),\ (#x " set autoop on"),\ (#x " set autoop off"),\ (#x " set display "),\ (#x " set email "),\ (#x " set greet "),\ (#x " set hide "),\ (#x " set hide email "),\ (#x " set hide email on"),\ (#x " set hide email off"),\ (#x " set hide status "),\ (#x " set hide status on"),\ (#x " set hide status off"),\ (#x " set hide usermask "),\ (#x " set hide usermask on"),\ (#x " set hide usermask off"),\ (#x " set hide quit "),\ (#x " set hide quit on"),\ (#x " set hide quit off"),\ (#x " set keepmodes "),\ (#x " set keepmodes on"),\ (#x " set keepmodes off"),\ (#x " set kill "),\ (#x " set kill on"),\ (#x " set kill quick"),\ (#x " set kill immed"),\ (#x " set kill off"),\ (#x " set language "),\ (#x " set password "),\ (#x " set private "),\ (#x " set private on"),\ (#x " set private off"),\ (#x " set secure "),\ (#x " set secure on"),\ (#x " set secure off"),\ (#x " set url "),\ (#x " status"),\ (#x " status "),\ (#x " ungroup"),\ (#x " ungroup "),\ (#x " update") __SWIRC_BEGIN_DECLS void cmd_squery(CSTRING) NONNULL; //lint -sem(get_list_of_matching_squery_commands, r_null) PTEXTBUF get_list_of_matching_squery_commands(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/theme.c000066400000000000000000000325501501213070300165060ustar00rootroot00000000000000/* commands/theme.c -- management of themes on-the-fly Copyright (C) 2017-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #if defined(_lint) && defined(curl_easy_setopt) #undef curl_easy_setopt #endif #include #include "../assertAPI.h" #include "../config.h" #include "../errHand.h" #include "../filePred.h" #include "../interpreter.h" #include "../libUtils.h" #include "../main.h" #include "../nestHome.h" #include "../printtext.h" #include "../statusbar.h" #include "../strHand.h" #include "../strdup_printf.h" #include "../theme.h" #include "../titlebar.h" #include "i18n.h" #include "theme.h" /* this is not '../theme.h' */ #ifndef PATH_MAX #define PATH_MAX 1024 #endif static stringarray_t theme_cmds = { "install ", "install bx", "install nano", "install superkod", "install weechat", "list-remote", "set ", "set bx", "set nano", "set superkod", "set weechat", }; static THEME_INFO theme_info_array[MAX_NO_THEMES]; static void free_theme_info(PTHEME_INFO) NONNULL; static void install_theme(const char *) NONNULL; static void set_theme(const char *) NONNULL; static bool is_instruction_ok(const char *instruction) { if (instruction == NULL) return false; else if (strings_match(instruction, "install")) return true; else if (strings_match(instruction, "list-remote")) return true; else if (strings_match(instruction, "set")) return true; return false; } static void theme_info_array_init(void) { PTHEME_INFO ar_p; THEME_INFO_FOREACH(ar_p) { ar_p->filename = NULL; ar_p->version = NULL; ar_p->author = NULL; ar_p->email = NULL; ar_p->timestamp = NULL; ar_p->comment = NULL; } } static void free_theme_info(PTHEME_INFO info) { free_and_null(&info->filename); free_and_null(&info->version); free_and_null(&info->author); free_and_null(&info->email); free_and_null(&info->timestamp); free_and_null(&info->comment); } static void theme_info_array_deinit(void) { PTHEME_INFO ar_p; THEME_INFO_FOREACH(ar_p) { free_theme_info(ar_p); } } static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) { return fwrite(ptr, size, nmemb, ((FILE *) stream)); } void url_to_file(const char *url, const char *path) { CURL *curl_handle = NULL; CURLcode ret = CURLE_OK; FILE *pagefile = NULL; const char *failed_op = ""; // NOLINT errno = 0; if ((ret = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK) { failed_op = "curl_global_init"; goto err; } else if ((curl_handle = curl_easy_init()) == NULL) { failed_op = "curl_easy_init"; goto err; } else if ((ret = curl_easy_setopt(curl_handle, CURLOPT_URL, url)) != CURLE_OK) { failed_op = "curl_easy_setopt: CURLOPT_URL"; goto err; } else if ((ret = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L)) != CURLE_OK) { failed_op = "curl_easy_setopt: CURLOPT_VERBOSE"; goto err; } else if ((ret = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L)) != CURLE_OK) { failed_op = "curl_easy_setopt: CURLOPT_NOPROGRESS"; goto err; } else if ((ret = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data)) != CURLE_OK) { failed_op = "curl_easy_setopt: CURLOPT_WRITEFUNCTION"; goto err; } else if ((pagefile = xfopen(path, "w")) == NULL) { failed_op = "xfopen"; goto err; } else if ((ret = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile)) != CURLE_OK) { failed_op = "curl_easy_setopt: CURLOPT_WRITEDATA"; goto err; } else if ((ret = curl_easy_perform(curl_handle)) != CURLE_OK) { failed_op = "curl_easy_perform"; goto err; } curl_easy_cleanup(curl_handle); fclose(pagefile); return; err: if (curl_handle) curl_easy_cleanup(curl_handle); if (pagefile) fclose(pagefile); err_log(errno, "%s: %s: %s", __func__, failed_op, curl_easy_strerror(ret)); } bool get_next_line_from_file(FILE *fp, char **line) { const int LINE_MAX_LEN = 2048; if (fp == NULL || line == NULL) err_exit(EINVAL, "%s", __func__); if (*line) { free(*line); *line = NULL; } if ((*line = malloc(LINE_MAX_LEN)) == NULL) err_exit(ENOMEM, "%s", __func__); return (fgets(*line, LINE_MAX_LEN, fp) ? true : false); } static bool add_to_array(PTHEME_INFO info) { PTHEME_INFO ar_p; if (info == NULL) return false; THEME_INFO_FOREACH(ar_p) { if (ar_p->filename == NULL) { ar_p->filename = sw_strdup(info->filename); ar_p->version = sw_strdup(info->version); ar_p->author = sw_strdup(info->author); ar_p->email = sw_strdup(info->email); ar_p->timestamp = sw_strdup(info->timestamp); ar_p->comment = sw_strdup(info->comment); free_theme_info(info); return true; } } sw_assert_not_reached(); return false; } /*lint -sem(tokenize, r_null) */ static PTHEME_INFO tokenize(const char *string) { const char *filename, *version, *author, *email, *timestamp, *comment; char *state = ""; char *str_copy = sw_strdup(string); static THEME_INFO info = { NULL }; static const char delim[] = ":"; if ((filename = strtok_r(str_copy, delim, &state)) == NULL || (version = strtok_r(NULL, delim, &state)) == NULL || (author = strtok_r(NULL, delim, &state)) == NULL || (email = strtok_r(NULL, delim, &state)) == NULL || (timestamp = strtok_r(NULL, delim, &state)) == NULL || (comment = strtok_r(NULL, delim, &state)) == NULL) { free(str_copy); return NULL; } info.filename = sw_strdup(filename); info.version = sw_strdup(version); info.author = sw_strdup(author); info.email = sw_strdup(email); info.timestamp = sw_strdup(timestamp); info.comment = sw_strdup(comment); free(str_copy); return (&info); } static read_result_t read_db(const char *path, int *themes_read) { FILE *fp = NULL; char *line = NULL; read_result_t res = READ_INCOMPLETE; if (path == NULL || (fp = xfopen(path, "r")) == NULL) return FOPEN_FAILED; while (get_next_line_from_file(fp, &line)) { const char *cp = trim(&line[0]); adv_while_isspace(&cp); if (strings_match(cp, "") || *cp == '#') continue; if (!add_to_array(tokenize(cp))) { fclose(fp); free(line); return PARSE_ERROR; } if (themes_read) (*themes_read)++; } res = (feof(fp) ? READ_DB_OK : READ_INCOMPLETE); fclose(fp); free(line); return res; } static void clean_up(char *url, char *path) { free(url); if (path) { if (remove(path) != 0) err_log(errno, _("failed to remove: %s"), path); free(path); } theme_info_array_deinit(); } static bool theme_is_in_db(const char *name) { PRINTTEXT_CONTEXT ctx; PTHEME_INFO ar_p = NULL; THEME_INFO_FOREACH(ar_p) { if (ar_p->filename && name && strings_match(ar_p->filename, name)) return true; } printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "%s", _("no such theme in the database")); return false; } static void install_theme(const char *name) { PRINTTEXT_CONTEXT ctx; char *url, *dest; url = strdup_printf("%s%s%s%s", g_swircWebAddr, "themes/", name, g_theme_filesuffix); dest = strdup_printf("%s" SLASH "%s%s", g_home_dir, name, g_theme_filesuffix); url_to_file(url, dest); printtext_context_init(&ctx, g_active_window, (file_exists(dest) ? TYPE_SPEC1_SUCCESS : TYPE_SPEC1_FAILURE), true); if (ctx.spec_type == TYPE_SPEC1_SUCCESS) { printtext(&ctx, "%s", _("theme installed " "(use 'set' to activate it)")); } else { printtext(&ctx, "%s", _("failed to install the theme :-(")); } free(url); free(dest); } static void list_remote(void) { #define B1 Theme("notice_inner_b1") #define B2 Theme("notice_inner_b2") PRINTTEXT_CONTEXT ctx; PTHEME_INFO ar_p = NULL; printtext_context_init(&ctx, g_active_window, TYPE_SPEC2, true); THEME_INFO_FOREACH(ar_p) { if (ar_p->filename) { printtext(&ctx, "----- %s%s%c %cv%s%c %s%s%s%c%s -----", COLOR1, ar_p->filename, NORMAL, UNDERLINE, ar_p->version, NORMAL, B1, COLOR4, ar_p->comment, NORMAL, B2); printtext(&ctx, "%sAuthor%c: %s", COLOR2, NORMAL, ar_p->author); printtext(&ctx, "%sE-mail%c: %s", COLOR2, NORMAL, ar_p->email); printtext(&ctx, "%sAdded%c: %s", COLOR2, NORMAL, ar_p->timestamp); } } } static void set_theme(const char *name) { PRINTTEXT_CONTEXT ctx; char buf[PATH_MAX] = { '\0' }; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); if ((errno = sw_strcpy(buf, g_home_dir, sizeof buf)) != 0 || (errno = sw_strcat(buf, SLASH, sizeof buf)) != 0 || (errno = sw_strcat(buf, name, sizeof buf)) != 0 || (errno = sw_strcat(buf, g_theme_filesuffix, sizeof buf)) != 0) { printtext(&ctx, "error building name path (errno=%d)", errno); return; } else if (!is_regular_file(buf)) { printtext(&ctx, "%s", _("non-existent")); return; } theme_deinit(); theme_init(); theme_readit(buf, "r"); titlebar(" %s ", (g_active_window->title ? g_active_window->title : "")); statusbar_update_display_beta(); ctx.spec_type = TYPE_SPEC1_SUCCESS; printtext(&ctx, "%s", _("theme activated")); if ((errno = config_item_undef("theme")) != 0) err_log(errno, "%s: config_item_undef", __func__); if ((errno = config_item_install("theme", name)) != 0) err_log(errno, "%s: config_item_install", __func__); BZERO(buf, sizeof buf); if ((errno = sw_strcpy(buf, g_home_dir, sizeof buf)) != 0 || (errno = sw_strcat(buf, SLASH, sizeof buf)) != 0 || (errno = sw_strcat(buf, "swirc", sizeof buf)) != 0 || (errno = sw_strcat(buf, g_config_filesuffix, sizeof buf)) != 0) { ctx.spec_type = TYPE_SPEC1_FAILURE; printtext(&ctx, "error building path to swirc%s (errno=%d)", g_config_filesuffix, errno); printtext(&ctx, "error saving swirc%s", g_config_filesuffix); return; } config_do_save(buf, "w"); } /* * usage: /theme [install | list-remote | set] [name] */ void cmd_theme(const char *data) { char *db_path, *instruction, *name, *url; char *dcopy = sw_strdup(data); char *state = ""; int themes_read = 0; static const char cmd[] = "/theme"; if (strings_match(dcopy, "") || (instruction = strtok_r(dcopy, " ", &state)) == NULL) { printf_and_free(dcopy, "%s: missing arguments", cmd); return; } const bool has_second_arg = (name = strtok_r(NULL, " ", &state)) != NULL; if (strtok_r(NULL, " ", &state) != NULL) { printf_and_free(dcopy, "%s: implicit trailing data", cmd); return; } else if (!is_instruction_ok(instruction)) { printf_and_free(dcopy, "%s: bogus instruction!", cmd); return; } else if (!has_second_arg && (strings_match(instruction, "install") || strings_match(instruction, "set"))) { printf_and_free(dcopy, "%s: missing arguments", cmd); return; } url = strdup_printf("%s%s", g_swircWebAddr, "themes/themes"); db_path = strdup_printf("%s" SLASH "%s", g_tmp_dir, "themes"); theme_info_array_init(); url_to_file(url, db_path); switch (read_db(db_path, &themes_read)) { case FOPEN_FAILED: printf_and_free(dcopy, "%s: cannot open database", cmd); clean_up(url, db_path); return; case PARSE_ERROR: printf_and_free(dcopy, "%s: failed to read database", cmd); clean_up(url, db_path); return; case READ_INCOMPLETE: printf_and_free(dcopy, "%s: end-of-file indicator not set", cmd); clean_up(url, db_path); return; case READ_DB_OK: default: break; } if (strings_match(instruction, "install")) { if (isValid(name) && theme_is_in_db(name)) install_theme(name); } else if (strings_match(instruction, "list-remote")) { list_remote(); } else if (strings_match(instruction, "set")) { if (isValid(name) && (strings_match(name, "default") || theme_is_in_db(name))) set_theme(name); } else { sw_assert_not_reached(); } free(dcopy); clean_up(url, db_path); } PTEXTBUF get_list_of_matching_theme_cmds(const char *search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(theme_cmds); i++) { const char *cmd = theme_cmds[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } swirc-3.5.5/src/commands/theme.h000066400000000000000000000014531501213070300165110ustar00rootroot00000000000000#ifndef SRC_COMMANDS_THEME_H_ #define SRC_COMMANDS_THEME_H_ #include /* FILE */ #include "../textBuffer.h" typedef enum { FOPEN_FAILED, PARSE_ERROR, READ_DB_OK, READ_INCOMPLETE } read_result_t; #define MAX_NO_THEMES 101 #define THEME_INFO_FOREACH(ar_p)\ for (ar_p = &theme_info_array[0];\ ar_p < &theme_info_array[MAX_NO_THEMES];\ ar_p++) typedef struct tagTHEME_INFO { char *filename; char *version; char *author; char *email; char *timestamp; char *comment; } THEME_INFO, *PTHEME_INFO; /*lint -sem(get_list_of_matching_theme_cmds, r_null) */ __SWIRC_BEGIN_DECLS void cmd_theme(const char *); PTEXTBUF get_list_of_matching_theme_cmds(const char *); bool get_next_line_from_file(FILE *, char **); void url_to_file(const char *, const char *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/topic.c000066400000000000000000000014451501213070300165210ustar00rootroot00000000000000#include "common.h" #include "../dataClassify.h" #include "../icb.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "topic.h" /* * usage: /topic [new topic] */ void cmd_topic(const char *data) { if (strings_match(data, "") && is_irc_channel(ACTWINLABEL)) { if (g_icb_mode) icb_send_topic(""); else (void) net_send("TOPIC %s", ACTWINLABEL); } else if (!strings_match(data, "") && is_irc_channel(ACTWINLABEL)) { if (g_icb_mode) icb_send_topic(data); else (void) net_send("TOPIC %s :%s", ACTWINLABEL, data); } else { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "/topic: switch to a channel in order to set a " "new topic for it"); } } swirc-3.5.5/src/commands/topic.h000066400000000000000000000001231501213070300165160ustar00rootroot00000000000000#ifndef CMD_TOPIC_H #define CMD_TOPIC_H void cmd_topic(const char *data); #endif swirc-3.5.5/src/commands/voice.c000066400000000000000000000053441501213070300165120ustar00rootroot00000000000000/* Command voice and devoice Copyright (C) 2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "voice.h" static const char err1[] = "missing arguments"; static const char err2[] = "invalid nickname"; static const char err3[] = "active window isn't an irc channel"; /* * usage: /voice */ void cmd_voice(CSTRING data) { static const char cmd[] = "/voice"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_valid_nickname(data)) { printtext_print("err", "%s: %s", cmd, err2); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err3); return; } (void) net_send("MODE %s +v %s", ACTWINLABEL, data); } /* * usage: /devoice */ void cmd_devoice(CSTRING data) { static const char cmd[] = "/devoice"; if (strings_match(data, "")) { printtext_print("err", "%s: %s", cmd, err1); return; } else if (!is_valid_nickname(data)) { printtext_print("err", "%s: %s", cmd, err2); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: %s", cmd, err3); return; } (void) net_send("MODE %s -v %s", ACTWINLABEL, data); } swirc-3.5.5/src/commands/voice.h000066400000000000000000000002131501213070300165050ustar00rootroot00000000000000#ifndef CMD_VOICE_H #define CMD_VOICE_H __SWIRC_BEGIN_DECLS void cmd_voice(CSTRING); void cmd_devoice(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/wholeft.cpp000066400000000000000000000074401501213070300174140ustar00rootroot00000000000000/* The wholeft command Copyright (C) 2024-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "../dataClassify.h" #include "../main.h" #include "../netsplit.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "../window.h" #include "wholeft.h" static bool netsplit_chk(CSTRING channel) { const std::vector &netsplit_db = netsplit_get_db(); for (auto it = netsplit_db.begin(); it != netsplit_db.end(); ++it) { immutable_cp_t db_chan = (*it)->channel.c_str(); if (strings_match_ignore_case(channel, db_chan)) return true; } return false; } static void pr_wholeft(const netsplit *ns) { PRINTTEXT_CONTEXT ctx; STRING list; if (ns == nullptr || ns->nicks.empty()) return; list = sw_strdup(""); for (const std::string &nick : ns->nicks) { std::string str(nick); str.insert(0, " "); realloc_strcat(&list, str.c_str()); } const struct netsplit_context ns_ctx(ns->channel.c_str(), ns->server[0].c_str(), ns->server[1].c_str()); printtext_context_init(&ctx, nullptr, TYPE_SPEC1, true); if ((ctx.window = window_by_label(ns_ctx.chan)) == nullptr) ctx.window = g_status_window; printtext(&ctx, "%s%s%s %s %s %s - the following users left:%s", LEFT_BRKT, ns_ctx.chan, RIGHT_BRKT, ns_ctx.serv1, THE_SPEC2, ns_ctx.serv2, list); free(list); } static void wholeft(CSTRING channel) { const std::vector &netsplit_db = netsplit_get_db(); for (auto it = netsplit_db.begin(); it != netsplit_db.end(); ++it) { immutable_cp_t db_chan = (*it)->channel.c_str(); if (strings_match_ignore_case(channel, db_chan) && (*it)->has_announced_split() && !(*it)->join_begun()) pr_wholeft(*it); } } /* * usage: /wholeft */ void cmd_wholeft(CSTRING data) { static chararray_t cmd = "/wholeft"; if (!strings_match(data, "")) { printtext_print("err", "%s: implicit trailing data", cmd); return; } else if (!is_irc_channel(ACTWINLABEL)) { printtext_print("err", "%s: the active window isn't an irc " "channel", cmd); return; } else if (netsplit_db_empty()) { printtext_print("err", "%s: there are no netsplits at the " "moment", cmd); return; } else if (!netsplit_chk(ACTWINLABEL)) { printtext_print("err", "%s: there are no netsplits for %s", cmd, ACTWINLABEL); return; } wholeft(ACTWINLABEL); } swirc-3.5.5/src/commands/wholeft.h000066400000000000000000000001661501213070300170570ustar00rootroot00000000000000#ifndef CMD_WHOLEFT_H #define CMD_WHOLEFT_H __SWIRC_BEGIN_DECLS void cmd_wholeft(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/commands/znc.cpp000066400000000000000000000135061501213070300165360ustar00rootroot00000000000000/* commands/znc.cpp Copyright (C) 2020-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include #include "../errHand.h" #include "../main.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "znc.h" _Atomic(bool) g_invoked_by_znc_jump_net = false; static stringarray_t znc_commands = { "AddMOTD ", "AddNetwork ", "AddPort ", "AddServer ", "AddTrustedServerFingerprint ", "Attach", "Broadcast", "ClearAllBuffers", "ClearAllChannelBuffers", "ClearAllQueryBuffers", "ClearBindHost", "ClearBuffer ", "ClearMOTD", "ClearUserBindHost", "Connect", "DelNetwork ", "DelPort", "DelServer ", "DelTrustedServerFingerprint ", "Detach", "DisableChan", "Disconnect", "EnableChan", "Jump", "JumpNetwork ", "ListAllUserNetworks", "ListAvailMods", "ListChans", "ListClients", "ListMods", "ListNetworks", "ListNicks", "ListPorts", "ListServers", "ListTrustedServerFingerprints", "ListUsers", "LoadMod ", "MoveNetwork", "PlayBuffer ", "Rehash", "ReloadMod", "Restart", "SaveConfig", "SetBindHost", "SetBuffer ", "SetMOTD ", "SetUserBindHost", "ShowBindHost", "ShowMOTD", "Shutdown", "Topics", "Traffic", "UnloadMod", "UpdateMod ", "Uptime", "Version", // In lowercase as well... "addmotd ", "addnetwork ", "addport ", "addserver ", "addtrustedserverfingerprint ", "attach", "broadcast", "clearallbuffers", "clearallchannelbuffers", "clearallquerybuffers", "clearbindhost", "clearbuffer ", "clearmotd", "clearuserbindhost", "connect", "delnetwork ", "delport", "delserver ", "deltrustedserverfingerprint ", "detach", "disablechan", "disconnect", "enablechan", "jump", "jumpnetwork ", "listallusernetworks", "listavailmods", "listchans", "listclients", "listmods", "listnetworks", "listnicks", "listports", "listservers", "listtrustedserverfingerprints", "listusers", "loadmod ", "movenetwork", "playbuffer ", "rehash", "reloadmod", "restart", "saveconfig", "setbindhost", "setbuffer ", "setmotd ", "setuserbindhost", "showbindhost", "showmotd", "shutdown", "topics", "traffic", "unloadmod", "updatemod ", "uptime", "version", }; static void send_cmd(CSTRING p_module, CSTRING p_command) { char v_module[80] = { '\0' }; static chararray_t array = "JumpNetwork "; static const size_t size = sizeof array - 1; if (sw_strcpy(v_module, p_module, sizeof v_module) != 0) throw std::runtime_error("string copying error"); else if (net_send("PRIVMSG %s :%s", strToLower(v_module), p_command) < 0) throw std::runtime_error("cannot send"); else if (!strncasecmp(p_command, array, size) && !strings_match(&p_command[size], "") && strings_match(v_module, "*status")) g_invoked_by_znc_jump_net = true; else return; } /* * usage: /znc [*module] */ void cmd_znc(CSTRING data) { STRING dcopy; bool written_linefeed = false; static chararray_t cmd = "/znc"; if (strings_match(data, "")) { printtext_print("err", "%s: missing arguments", cmd); return; } if (*(dcopy = sw_strdup(data)) == '*') written_linefeed = (strFeed(dcopy, 1) == 1); std::istringstream input(dcopy); free(dcopy); try { std::string token; std::vector tokens; while (std::getline(input, token)) tokens.push_back(token); if (!written_linefeed) { if (tokens.size() != 1) { throw std::runtime_error("bogus number of " "tokens (expected 1)"); } else send_cmd("*status", tokens.at(0).c_str()); } else if (tokens.size() != 2) { throw std::runtime_error("bogus number of tokens " "(expected 2)"); } else if (tokens.at(0).at(0) != '*') { throw std::runtime_error("bogus module name"); } else { send_cmd(tokens.at(0).c_str(), tokens.at(1).c_str()); } } catch (const std::runtime_error &e) { printtext_print("err", "%s: %s", cmd, e.what()); } catch (...) { printtext_print("err", "%s: %s", cmd, "unknown exception"); } } PTEXTBUF get_list_of_matching_znc_commands(CSTRING search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); for (size_t i = 0; i < ARRAY_SIZE(znc_commands); i++) { CSTRING cmd = znc_commands[i]; if (!strncmp(search_var, cmd, varlen)) textBuf_emplace_back(__func__, matches, cmd, 0); } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } swirc-3.5.5/src/commands/znc.h000066400000000000000000000004051501213070300161750ustar00rootroot00000000000000#ifndef CMD_ZNC_H #define CMD_ZNC_H #include "atomicops.h" #include "../textBuffer.h" __SWIRC_BEGIN_DECLS extern _Atomic(bool) g_invoked_by_znc_jump_net; void cmd_znc(CSTRING); PTEXTBUF get_list_of_matching_znc_commands(CSTRING); __SWIRC_END_DECLS #endif swirc-3.5.5/src/config.cpp000066400000000000000000000540171501213070300154120ustar00rootroot00000000000000/* User configuration Copyright (C) 2012-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #if UNIX #include #endif #if __OpenBSD__ #include #endif #include "config.h" #include "errHand.h" #include "interpreter.h" #include "libUtils.h" #include "main.h" #include "nestHome.h" #include "printtext.h" #include "readline.h" #include "spell.h" #include "strHand.h" #include "theme.h" #include #ifdef HAVE_LIBIDN #include #include #else #pragma message("No GNU libidn") #endif #include "commands/sasl.h" #define ENTRY_FOREACH()\ for (PCONF_HTBL_ENTRY *entry_p = &hash_table[0];\ entry_p < &hash_table[ARRAY_SIZE(hash_table)];\ entry_p++) #define FOREACH_CDV()\ for (struct tagConfDefValues *cdv_p = &ConfDefValues[0];\ cdv_p < &ConfDefValues[ARRAY_SIZE(ConfDefValues)];\ cdv_p++) #define hash(str) hash_pjw_g(str, false, ARRAY_SIZE(hash_table)) /* Objects with internal linkage ============================= */ static PCONF_HTBL_ENTRY hash_table[300] = { NULL }; static size_t longest_set_count = 0; static struct tagConfDefValues { const char *setting_name; enum setting_type type; short int padding; const char *value; } ConfDefValues[] = { { "nickname", TYPE_STRING, 2, "" }, { "nickname_aliases", TYPE_STRING, 1, "" }, { "alt_nick", TYPE_STRING, 2, "" }, { "username", TYPE_STRING, 2, "" }, { "real_name", TYPE_STRING, 2, "" }, { "ctcp_reply", TYPE_BOOLEAN, 2, "yes" }, { "ctcp_userinfo", TYPE_STRING, 2, "No info set" }, { "dcc", TYPE_BOOLEAN, 3, "yes" }, { "dcc_cipher_suite", TYPE_STRING, 1, "compat" }, { "dcc_own_ip", TYPE_STRING, 2, "" }, { "dcc_port", TYPE_INTEGER, 2, "8080" }, { "dcc_upload_dir", TYPE_STRING, 2, "" }, { "ftp_host", TYPE_STRING, 2, "" }, { "ftp_port", TYPE_INTEGER, 2, "21" }, { "ftp_user", TYPE_STRING, 2, "anonymous" }, { "ftp_pass", TYPE_STRING, 2, "anonymous" }, { "ftp_upload_dir", TYPE_STRING, 2, "" }, { "identd", TYPE_BOOLEAN, 3, "no" }, { "identd_fakenames", TYPE_BOOLEAN, 1, "no" }, { "identd_port", TYPE_INTEGER, 2, "113" }, { "sasl", TYPE_BOOLEAN, 2, "no" }, { "sasl_mechanism", TYPE_STRING, 1, "PLAIN" }, { "sasl_username", TYPE_STRING, 1, "" }, { "sasl_password", TYPE_STRING, 1, "" }, { "sasl_x509", TYPE_STRING, 1, "client.pem" }, { "socks", TYPE_BOOLEAN, 2, "no" }, { "socks_atyp", TYPE_INTEGER, 1, "0" }, { "socks_host", TYPE_STRING, 1, "localhost" }, { "socks_port", TYPE_STRING, 1, "9050" }, { "spell", TYPE_BOOLEAN, 2, "yes" }, { "spell_lang", TYPE_STRING, 1, "en_US" }, { "spell_syswide", TYPE_BOOLEAN, 1, "yes" }, { "chanserv_host", TYPE_STRING, 1, "services." }, { "nickserv_host", TYPE_STRING, 1, "services." }, { "qbot_host", TYPE_STRING, 1, "CServe.quakenet.org" }, /* IRCv3 */ { "account_notify", TYPE_BOOLEAN, 2, "yes" }, { "account_tag", TYPE_BOOLEAN, 2, "yes" }, { "away_notify", TYPE_BOOLEAN, 2, "no" }, { "batch", TYPE_BOOLEAN, 3, "yes" }, { "chghost", TYPE_BOOLEAN, 3, "yes" }, { "extended_join", TYPE_BOOLEAN, 2, "yes" }, { "invite_notify", TYPE_BOOLEAN, 2, "no" }, { "multi_prefix", TYPE_BOOLEAN, 2, "yes" }, { "server_time", TYPE_BOOLEAN, 2, "yes" }, { "part_message", TYPE_STRING, 1, "" }, { "quit_message", TYPE_STRING, 1, "Swirc IRC client" }, { "reconnect_backoff_delay", TYPE_INTEGER, 1, STRINGIFY(RECONNECT_BACKOFF_DELAY_DEFAULT) }, { "reconnect_delay", TYPE_INTEGER, 2, STRINGIFY(RECONNECT_DELAY_DEFAULT) }, { "reconnect_delay_max", TYPE_INTEGER, 1, STRINGIFY(RECONNECT_DELAY_MAX_DEFAULT) }, { "reconnect_retries", TYPE_INTEGER, 1, STRINGIFY(RECONNECT_RETRIES_DEFAULT) }, { "auto_op_yourself", TYPE_BOOLEAN, 2, "yes" }, { "awaymsgs_in_privconv", TYPE_BOOLEAN, 2, "yes" }, { "beeps", TYPE_BOOLEAN, 4, "yes" }, { "cipher_suite", TYPE_STRING, 3, "compat" }, { "cmd_hist_size", TYPE_INTEGER, 3, "50" }, { "cmd_type_prot", TYPE_BOOLEAN, 3, "yes" }, { "connection_timeout", TYPE_INTEGER, 2, "45" }, { "hostname_checking", TYPE_BOOLEAN, 2, "yes" }, { "iconv_conversion", TYPE_BOOLEAN, 2, "yes" }, { "joins_parts_quits", TYPE_BOOLEAN, 2, "no" }, { "kick_close_window", TYPE_BOOLEAN, 2, "yes" }, { "max_chat_windows", TYPE_INTEGER, 2, "60" }, { "mouse", TYPE_BOOLEAN, 4, "no" }, { "mouse_events", TYPE_STRING, 3, "wheel" }, { "notifications", TYPE_BOOLEAN, 3, "yes" }, { "show_ping_pong", TYPE_BOOLEAN, 3, "no" }, { "skip_motd", TYPE_BOOLEAN, 3, "no" }, { "ssl_verify_peer", TYPE_BOOLEAN, 3, "yes" }, { "startup_greeting", TYPE_BOOLEAN, 2, "yes" }, { "textbuffer_size_absolute", TYPE_INTEGER, 1, "1500" }, { "theme", TYPE_STRING, 4, "default" }, }; /* -------------------------------------------------- */ /*lint -sem(get_hash_table_entry, r_null) */ static PCONF_HTBL_ENTRY get_hash_table_entry(const char *name) { PCONF_HTBL_ENTRY entry; if (name == NULL) return NULL; for (entry = hash_table[hash(name)]; entry != NULL; entry = entry->next) { if (strings_match(name, entry->name)) return entry; } return NULL; } static const char * get_setting_type(const struct tagConfDefValues *cdv) { if (cdv->type == TYPE_BOOLEAN) return "bool"; else if (cdv->type == TYPE_INTEGER) return "int"; else if (cdv->type == TYPE_STRING) return "string"; return "unknown"; } static std::string get_string(const char *name, const char *type) { #define B1 Theme("notice_inner_b1") #define B2 Theme("notice_inner_b2") std::string str(name); (void) str.append(B1).append(type).append(B2); (void) str.append(" ").append(Theme("notice_sep")).append(" "); if (!strings_match(name, "ftp_pass") && !strings_match(name, "sasl_password")) str.append(Config(name)); else str.append(xstrnlen(Config(name), 80), '*'); return str; } static void hInstall(const char *name, const char *value) { PCONF_HTBL_ENTRY item; const bool has_no_value = (value == NULL || *value == '\0'); unsigned int hashval; item = static_cast(xcalloc(sizeof *item, 1)); item->name = sw_strdup(name); item->value = sw_strdup(has_no_value ? "" : value); hashval = hash(name); item->next = hash_table[hashval]; hash_table[hashval] = item; } static void hUndef(PCONF_HTBL_ENTRY) NONNULL; static void hUndef(PCONF_HTBL_ENTRY entry) { PCONF_HTBL_ENTRY *indirect; indirect = addrof(hash_table[hash(entry->name)]); while (*indirect != entry) indirect = addrof((*indirect)->next); *indirect = entry->next; free(entry->name); free(entry->value); free(entry); } static void init_missing_to_defs(void) { FOREACH_CDV() { if (get_hash_table_entry(cdv_p->setting_name) == NULL) hInstall(cdv_p->setting_name, cdv_p->value); } } static bool is_recognized_setting(const char *setting_name) { if (setting_name == NULL || strings_match(setting_name, "")) return false; FOREACH_CDV() { if (strings_match(setting_name, cdv_p->setting_name)) return true; } return false; } static void output_value_for_specific_setting(const char *setting) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC3, true); FOREACH_CDV() { if (strings_match(setting, cdv_p->setting_name)) { printtext(&ctx, "%s", get_string(cdv_p->setting_name, get_setting_type(cdv_p)).c_str()); return; } } print_and_free("/set: no such setting", NULL); } void output_values_for_all_settings(void) { PRINTTEXT_CONTEXT ctx; printtext_context_init(&ctx, g_active_window, TYPE_SPEC3, true); FOREACH_CDV() { printtext(&ctx, "%s", get_string(cdv_p->setting_name, get_setting_type(cdv_p)).c_str()); } } void output_values_for_all_settings_v2(void) { char *cp[2]; char fmt[2][100]; const int i = static_cast(longest_set_count); int ret[2]; cp[0] = &fmt[0][0]; cp[1] = &fmt[1][0]; ret[0] = snprintf(cp[0], sizeof(fmt[0]), "%%%ds %%7s %%s", i); ret[1] = snprintf(cp[1], sizeof(fmt[1]), "%%%ds %%7s %%s", i); if (ret[0] < 0 || static_cast(ret[0]) >= sizeof(fmt[0]) || ret[1] < 0 || static_cast(ret[1]) >= sizeof(fmt[1])) { printtext_print("err", "snprintf() error"); return; } printtext_print("none", cp[0], "Name", "Type", "Value"); printtext_print("none", cp[0], "----", "----", "-----"); FOREACH_CDV() { const char *name = cdv_p->setting_name; const char *type = get_setting_type(cdv_p); std::string value(""); if (!strings_match(name, "ftp_pass") && !strings_match(name, "sasl_password")) value.assign(Config(name)); else value.assign(xstrnlen(Config(name), 20), '*'); printtext_print("none", cp[1], name, type, value.c_str()); } } static void set_value_for_setting_ok_hook(const char *setting, const char *value) { if (strings_match(setting, "dcc_upload_dir") || strings_match(setting, "ftp_upload_dir")) { #if defined(OpenBSD) && OpenBSD >= 201811 printtext_print("warn", "OpenBSD uses unveil() -- " "restart needed"); #else /* null */; #endif } else if (strings_match(setting, "mouse") || strings_match(setting, "mouse_events")) { readline_mouse_init(); } else if (strings_match(setting, "spell_lang") || strings_match(setting, "spell_syswide")) { #ifdef HAVE_HUNSPELL spell_deinit(); spell_init(true); #else /* null */; #endif } UNUSED_PARAM(value); } static bool set_value_for_setting(const char *setting, const char *value, const char **err_reason) { FOREACH_CDV() { if (strings_match(setting, cdv_p->setting_name)) { if (cdv_p->type == TYPE_BOOLEAN && !strings_match_ignore_case(value, "on") && !strings_match_ignore_case(value, "true") && !strings_match_ignore_case(value, "yes") && !strings_match_ignore_case(value, "off") && !strings_match_ignore_case(value, "false") && !strings_match_ignore_case(value, "no")) { *err_reason = "booleans must be on, true, yes, " "off, false or no"; return false; } else if (cdv_p->type == TYPE_INTEGER && !is_numeric(value)) { *err_reason = "integer not all numeric"; return false; } else if (config_item_undef(setting) != 0) { *err_reason = "config_item_undef"; return false; } else if (config_item_install(setting, value) != 0) { *err_reason = "config_item_install"; return false; } config_do_save(g_config_file, "w"); set_value_for_setting_ok_hook(setting, value); return true; } } *err_reason = "no such setting"; return false; } static void try_to_set_value_for_setting(const char *setting, const char *value) { PRINTTEXT_CONTEXT ctx; const char *err_reason = "no error"; printtext_context_init(&ctx, g_active_window, TYPE_SPEC1_SUCCESS, true); FOREACH_CDV() { if (strings_match(setting, cdv_p->setting_name)) { if (!set_value_for_setting(setting, value, &err_reason)) print_and_free(err_reason, NULL); else printtext(&ctx, "ok"); return; } } print_and_free("/set: no such setting", NULL); } static void write_config_header(FILE *fp) { write_to_stream(fp, "# -*- mode: conf; -*-\n#\n# Swirc %s -- " "default config\n", g_swircVersion); write_to_stream(fp, "# Automatically generated at %s\n\n", current_time("%c")); } /* -------------------------------------------------- */ PTEXTBUF get_list_of_matching_settings(const char *search_var) { PTEXTBUF matches = textBuf_new(); const size_t varlen = strlen(search_var); FOREACH_CDV() { if (!strncmp(search_var, cdv_p->setting_name, varlen)) { textBuf_emplace_back(__func__, matches, cdv_p->setting_name, 0); } } if (textBuf_size(matches) == 0) { textBuf_destroy(matches); return NULL; } return matches; } void config_init(void) { size_t len = 0; ENTRY_FOREACH() { *entry_p = NULL; } FOREACH_CDV() { if ((len = strlen(cdv_p->setting_name)) > longest_set_count) longest_set_count = len; } } void config_deinit(void) { PCONF_HTBL_ENTRY p, tmp; ENTRY_FOREACH() { for (p = *entry_p; p != NULL; p = tmp) { tmp = p->next; hUndef(p); } } } void config_lock_hash_table(void) { #if defined(UNIX) if (mlock(addrof(hash_table[0]), sizeof hash_table) == -1) err_log(errno, "%s: mlock", __func__); #elif defined(WIN32) if (!VirtualLock(addrof(hash_table[0]), sizeof hash_table)) { err_log(0, "%s: VirtualLock: %s", __func__, errdesc_by_last_err()); } #endif } void config_unlock_hash_table(void) { #if defined(UNIX) if (munlock(addrof(hash_table[0]), sizeof hash_table) == -1) err_log(errno, "%s: munlock", __func__); #elif defined(WIN32) if (!VirtualUnlock(addrof(hash_table[0]), sizeof hash_table)) { err_log(0, "%s: VirtualUnlock: %s", __func__, errdesc_by_last_err()); } #endif } bool config_bool(const char *setting_name, bool fallback_default) { PCONF_HTBL_ENTRY item; if (setting_name == NULL) err_exit(EINVAL, "%s", __func__); for (item = hash_table[hash(setting_name)]; item != NULL; item = item->next) { if (strings_match(setting_name, item->name)) { if (bool_true(item->value)) return true; else if (bool_false(item->value)) return false; else break; } } err_log(EINVAL, "warning: setting %s (bool): falling back to the " "default", setting_name); return fallback_default; } char * Config_mod(const char *setting_name) { PCONF_HTBL_ENTRY item; if (setting_name == NULL) return NULL; for (item = hash_table[hash(setting_name)]; item != NULL; item = item->next) { if (strings_match(setting_name, item->name)) return item->value; } return NULL; } const char * Config(const char *setting_name) { PCONF_HTBL_ENTRY item; if (setting_name == NULL) return ""; for (item = hash_table[hash(setting_name)]; item != NULL; item = item->next) { if (strings_match(setting_name, item->name)) { if (!strings_match(setting_name, "sasl_password")) return item->value; else if (*(item->value) == g_decrypted_pass_sym || *(item->value) == g_encrypted_pass_sym || *(item->value) == g_unencrypted_pass_sym) return addrof(item->value[1]); else return ""; } } return ""; } errno_t config_item_install(const char *name, const char *value) { if (name == NULL || value == NULL) return EINVAL; else if (get_hash_table_entry(name)) return EBUSY; else hInstall(name, value); return 0; } errno_t config_item_undef(const char *name) { PCONF_HTBL_ENTRY entry; if ((entry = get_hash_table_entry(name)) == NULL) return ENOENT; hUndef(entry); return 0; } long int config_integer(const struct integer_context *ctx) { PCONF_HTBL_ENTRY item; long int val; if (ctx == NULL) err_exit(EINVAL, "%s", __func__); for (item = hash_table[hash(ctx->setting_name)]; item != NULL; item = item->next) { if (strings_match(ctx->setting_name, item->name)) { if (getval_strtol(item->value, ctx->lo_limit, ctx->hi_limit, &val)) return val; else break; } } err_log(ERANGE, "warning: setting %s (%ld-%ld): fallback value is %ld", ctx->setting_name, ctx->lo_limit, ctx->hi_limit, ctx->fallback_default); return ctx->fallback_default; } void config_create(const char *path, const char *mode) { FILE *fp; fp = fopen_exit_on_error(path, mode); write_config_header(fp); FOREACH_CDV() { write_setting(fp, cdv_p->setting_name, cdv_p->value, true, cdv_p->padding); } fclose_ensure_success(fp); } void config_do_save(const char *path, const char *mode) { FILE *fp; fp = fopen_exit_on_error(path, mode); write_config_header(fp); FOREACH_CDV() { if (!strings_match(cdv_p->setting_name, "sasl_password")) { write_setting(fp, cdv_p->setting_name, Config (cdv_p->setting_name), true, cdv_p->padding); } else { char c; std::string str(""); if ((c = get_sasl_passwd_type()) == g_decrypted_pass_sym) { /* * Don't save the decrypted SASL pass */ str.push_back(g_encrypted_pass_sym); str.append(g_encrypted_sasl_pass ? g_encrypted_sasl_pass : ""); } else { str.push_back(c); str.append(Config(cdv_p->setting_name)); } write_setting(fp, cdv_p->setting_name, str.c_str(), true, cdv_p->padding); } } fclose_ensure_success(fp); } void config_readit(const char *path, const char *mode) { FILE *fp; fp = fopen_exit_on_error(path, mode); Interpreter_processAllLines(fp, path, is_recognized_setting, config_item_install); if (feof(fp)) { fclose_ensure_success(fp); init_missing_to_defs(); } else if (ferror(fp)) { err_quit("%s: %s", __func__, g_fgets_nullret_err1); } else { err_msg("%s: %s", __func__, g_fgets_nullret_err2); abort(); } } char get_sasl_passwd_type(void) { PCONF_HTBL_ENTRY item; static const char setting_name[] = "sasl_password"; for (item = hash_table[hash(setting_name)]; item != NULL; item = item->next) { if (strings_match(setting_name, item->name)) { if (*(item->value) == g_decrypted_pass_sym || *(item->value) == g_encrypted_pass_sym || *(item->value) == g_unencrypted_pass_sym) return *(item->value); else break; } } // XXX: may want to return something else return g_unencrypted_pass_sym; } #ifdef HAVE_LIBIDN static const char * get_sasl(const char *what, char *buf, size_t bufsize) { Stringprep_profile_flags flags; char *sp_out = NULL; char *str = NULL; int ret; if (strings_match(Config(what), "")) { return NULL; } else if ((str = stringprep_locale_to_utf8(Config(what))) == NULL || sw_strcpy(buf, str, bufsize) != 0) { idn_free(str); return NULL; } flags = static_cast(0); // NOLINT if ((ret = stringprep_profile(str, &sp_out, "SASLprep", flags)) == STRINGPREP_OK && sw_strcpy(buf, sp_out, bufsize) == 0) { idn_free(sp_out); idn_free(str); return buf; } if (ret != STRINGPREP_OK) { err_log(0, "get_sasl: stringprep_profile: %s " "(using fallback solution...)", stringprep_strerror (static_cast(ret))); } idn_free(sp_out); idn_free(str); return buf; } #endif const char * config_get_normalized_sasl_username(void) { #ifdef HAVE_LIBIDN static char buf[SASL_USERNAME_MAXLEN] = { '\0' }; return get_sasl("sasl_username", &buf[0], ARRAY_SIZE(buf)); #else /* * !HAVE_LIBIDN */ if (strings_match(Config("sasl_username"), "")) return NULL; return Config("sasl_username"); #endif } const char * config_get_normalized_sasl_password(void) { #ifdef HAVE_LIBIDN static char buf[SASL_PASSWORD_MAXLEN] = { '\0' }; return get_sasl("sasl_password", &buf[0], ARRAY_SIZE(buf)); #else /* * !HAVE_LIBIDN */ if (strings_match(Config("sasl_password"), "")) return NULL; return Config("sasl_password"); #endif } /* -------------------------------------------------- */ /* * usage: /set [[setting] [value]] */ void cmd_set(const char *data) { char *dcopy; char *state = const_cast(""); const char *setting, *value; if (strings_match(data, "")) { output_values_for_all_settings_v2(); return; } dcopy = sw_strdup(data); (void) strFeed(dcopy, 1); if ((setting = strtok_r(dcopy, "\n", &state)) == NULL) printtext_print("err", "fatal error (shouldn't happen)"); else if ((value = strtok_r(NULL, "\n", &state)) == NULL) output_value_for_specific_setting(setting); else if (strings_match(setting, "sasl_password")) printtext_print("err", "please use /sasl"); else try_to_set_value_for_setting(setting, value); free(dcopy); } /* -------------------------------------------------- */ long int get_reconnect_backoff_delay(void) { struct integer_context ctx("reconnect_backoff_delay", 0, 99, RECONNECT_BACKOFF_DELAY_DEFAULT); return config_integer(&ctx); } long int get_reconnect_delay(void) { struct integer_context ctx("reconnect_delay", 0, 999, RECONNECT_DELAY_DEFAULT); return config_integer(&ctx); } long int get_reconnect_delay_max(void) { struct integer_context ctx("reconnect_delay_max", 0, 999, RECONNECT_DELAY_MAX_DEFAULT); return config_integer(&ctx); } long int get_reconnect_retries(void) { struct integer_context ctx("reconnect_retries", 0, 999, RECONNECT_RETRIES_DEFAULT); return config_integer(&ctx); } swirc-3.5.5/src/config.h000066400000000000000000000034051501213070300150520ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H #include "integer-context.h" #include "textBuffer.h" #define RECONNECT_BACKOFF_DELAY_DEFAULT 60 #define RECONNECT_DELAY_DEFAULT 10 #define RECONNECT_DELAY_MAX_DEFAULT 240 #define RECONNECT_RETRIES_DEFAULT 30 typedef struct tagCONF_HTBL_ENTRY { char *name; char *value; struct tagCONF_HTBL_ENTRY *next; } CONF_HTBL_ENTRY, *PCONF_HTBL_ENTRY; __SWIRC_BEGIN_DECLS void output_values_for_all_settings(void); void output_values_for_all_settings_v2(void); /*lint -sem(get_list_of_matching_settings, r_null) */ PTEXTBUF get_list_of_matching_settings(const char *); void config_init(void); void config_deinit(void); void config_lock_hash_table(void); void config_unlock_hash_table(void); /*lint -sem(Config_mod, r_null) */ bool config_bool(const char *, bool); char *Config_mod(const char *); const char *Config(const char *); errno_t config_item_install(const char *name, const char *value); errno_t config_item_undef(const char *name); long int config_integer(const struct integer_context *); void config_create(const char *path, const char *mode); void config_do_save(const char *path, const char *mode); void config_readit(const char *path, const char *mode); char get_sasl_passwd_type(void); #define SASL_USERNAME_MAXLEN 480 #define SASL_PASSWORD_MAXLEN 480 /*lint -sem(config_get_normalized_sasl_username, r_null) */ /*lint -sem(config_get_normalized_sasl_password, r_null) */ const char *config_get_normalized_sasl_username(void); const char *config_get_normalized_sasl_password(void); void cmd_set(const char *); long int get_reconnect_backoff_delay(void); long int get_reconnect_delay(void); long int get_reconnect_delay_max(void); long int get_reconnect_retries(void); __SWIRC_END_DECLS #endif /* CONFIG_H */ swirc-3.5.5/src/crypt.cpp000066400000000000000000000223201501213070300152760ustar00rootroot00000000000000/* crypt.cpp Copyright (C) 2022-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include "base64.h" #include "crypt.h" #include "errHand.h" #include "libUtils.h" #include "strHand.h" static void clean_up(EVP_CIPHER_CTX *ctx1, PCRYPT_CTX ctx2, cryptstr_t str, size_t size) { if (ctx1 != nullptr) EVP_CIPHER_CTX_free(ctx1); if (ctx2 != nullptr) { OPENSSL_cleanse(ctx2->key, sizeof ctx2->key); OPENSSL_cleanse(ctx2->iv, sizeof ctx2->iv); } if (str != nullptr && size > 0) OPENSSL_cleanse(str, size); free(str); } static const EVP_CIPHER * get_encrypt_alg() { return EVP_aes_256_ctr(); } /** * Decrypts a string. The storage is allocated on the heap and must be free()'d. * * @param[in] str The string to be decrypted which must be null- * terminated and encoded with base64 (and possibly with a * rot13 layer) * @param[in] password Decryption password * @param[in] rot13 Shall rot13 be ran on the string initially, yes/no? * * @return The decrypted string, or nullptr on error. */ STRING crypt_decrypt_str(CSTRING str, cryptstr_const_t password, const bool rot13) { CRYPT_CTX crypt_ctx; /* Key and IV */ EVP_CIPHER_CTX *cipher_ctx = nullptr; /* Cipher context */ STRING str_copy = nullptr; /* Non-const copy of 'str' */ bool error = false; /* True if an error occurred */ cryptstr_t decdat = nullptr; /* Decrypted data */ cryptstr_t decoded_str = nullptr; /* Base64 decoded string */ cryptstr_t out_str = nullptr; /* Returned on success */ int decdat_len = 0; /* Decrypted data length */ int decdat_size = 0; /* 'decdat' size */ int rem_bytes = 0; /* Remaining bytes */ try { int decode_len = 0; /* 'decoded_str' size */ int decode_ret = -1; /* Retval of b64_decode() */ if (str == nullptr || password == nullptr) throw std::runtime_error("invalid args"); str_copy = sw_strdup(str); if (rot13) (void) rot13_str(str_copy); if ((decode_len = crypt_get_base64_decode_length(str_copy)) <= 0) throw std::runtime_error("length error"); decoded_str = static_cast(xcalloc(decode_len, 1)); if ((decode_ret = b64_decode(str_copy, decoded_str, static_cast (decode_len))) == -1) throw std::runtime_error("base 64 error"); debug("decode_ret = %d", decode_ret); free_and_null(&str_copy); if (crypt_get_key_and_iv(password, &crypt_ctx) == -1) { throw std::runtime_error("unable to get key/iv"); } else if ((cipher_ctx = EVP_CIPHER_CTX_new()) == nullptr) { err_exit(ENOMEM, "%s: EVP_CIPHER_CTX_new", __func__); } else if (!EVP_DecryptInit_ex(cipher_ctx, get_encrypt_alg(), nullptr, addrof(crypt_ctx.key[0]), addrof(crypt_ctx.iv[0]))) { throw std::runtime_error("evp decrypt initialization " "failed"); } decdat_size = decode_len + EVP_CIPHER_CTX_block_size(cipher_ctx); decdat = static_cast(xcalloc(decdat_size, 1)); if (!EVP_DecryptUpdate(cipher_ctx, decdat, &decdat_len, decoded_str, decode_len)) { throw std::runtime_error("decryption failed!"); } else if (!EVP_DecryptFinal_ex(cipher_ctx, addrof(decdat[decdat_len]), &rem_bytes)) { throw std::runtime_error("decryption finalization " "failed!"); } decdat_len += rem_bytes; debug("%s: decdat_len: %d", __func__, decdat_len); debug("%s: decdat_size: %d", __func__, decdat_size); EVP_CIPHER_CTX_free(cipher_ctx); cipher_ctx = nullptr; out_str = static_cast(xmalloc(int_sum(decdat_len, 1))); out_str[decdat_len] = '\0'; memcpy(out_str, decdat, static_cast(decdat_len)); } catch (const std::exception &ex) { error = true; err_log(0, "%s: %s", __func__, ex.what()); } catch (...) { error = true; err_log(0, "%s: %s", __func__, "unknown exception!"); } clean_up(cipher_ctx, &crypt_ctx, decdat, static_cast (decdat_size)); free(str_copy); free(decoded_str); if (error) { free(out_str); return nullptr; } return reinterpret_cast(out_str); } /** * Encrypts a string. The storage is allocated on the heap and must be free()'d. * * @param[in] str Null-terminated plaintext * @param[in] password Encryption password * @param[in] rot13 Shall rot13 be used? * * @return The encrypted string, or nullptr on error. */ STRING crypt_encrypt_str(cryptstr_const_t str, cryptstr_const_t password, const bool rot13) { CRYPT_CTX crypt_ctx; /* Key and IV */ EVP_CIPHER_CTX *cipher_ctx = nullptr; /* Cipher context */ STRING b64str = nullptr; /* Base64 string */ bool error = false; /* True if an error occurred */ cryptstr_t encdat = nullptr; /* Encrypted data */ int encdat_len = 0; /* Encrypted data length */ int encdat_size = 0; /* 'encdat' size */ int rem_bytes = 0; /* Remaining bytes */ try { uint32_t size = 0; if (str == nullptr || password == nullptr) { throw std::runtime_error("invalid args"); } else if (crypt_get_key_and_iv(password, &crypt_ctx) == -1) { throw std::runtime_error("unable to get key/iv"); } else if ((cipher_ctx = EVP_CIPHER_CTX_new()) == nullptr) { err_exit(ENOMEM, "%s: EVP_CIPHER_CTX_new", __func__); } else if (!EVP_EncryptInit_ex(cipher_ctx, get_encrypt_alg(), nullptr, addrof(crypt_ctx.key[0]), addrof(crypt_ctx.iv[0]))) { throw std::runtime_error("evp encrypt initialization " "failed"); } encdat_size = crypt_strlen(str) + EVP_CIPHER_CTX_block_size(cipher_ctx) + 1; encdat = static_cast(xmalloc(encdat_size)); if (!EVP_EncryptUpdate(cipher_ctx, encdat, &encdat_len, str, crypt_strlen(str) + 1)) { throw std::runtime_error("encryption failed!"); } else if (!EVP_EncryptFinal_ex(cipher_ctx, addrof(encdat[encdat_len]), &rem_bytes)) { throw std::runtime_error("encryption finalization " "failed!"); } encdat_len += rem_bytes; debug("%s: encdat_len: %d", __func__, encdat_len); debug("%s: encdat_size: %d", __func__, encdat_size); EVP_CIPHER_CTX_free(cipher_ctx); cipher_ctx = nullptr; if ((size = crypt_get_base64_encode_length(encdat_len)) == 0) throw std::runtime_error("base64 length error"); b64str = static_cast(xmalloc(size)); b64str[size - 1] = '\0'; if (b64_encode(encdat, encdat_len, b64str, size) == -1) throw std::runtime_error("base64 error"); } catch (const std::exception &ex) { error = true; err_log(0, "%s: %s", __func__, ex.what()); } catch (...) { error = true; err_log(0, "%s: %s", __func__, "unknown exception!"); } clean_up(cipher_ctx, &crypt_ctx, encdat, static_cast (encdat_size)); if (error) { free(b64str); return nullptr; } return (rot13 ? rot13_str(b64str) : b64str); } void crypt_freezero(void *vp, const size_t len) { if (vp != nullptr && len > 0) OPENSSL_cleanse(vp, len); free(vp); } int crypt_get_base64_decode_length(CSTRING str) { int len; if (str == nullptr || strings_match(str, "") || (len = b64_decode(str, nullptr, 0)) < 0) return 0; return int_sum(len, 1); } uint32_t crypt_get_base64_encode_length(const uint32_t n) { return (((4 * n / 3) + 3) & ~3) + 1; } int crypt_get_key_and_iv(cryptstr_const_t password, PCRYPT_CTX ctx) { if (password == nullptr || ctx == nullptr) return -1; return (EVP_BytesToKey(get_encrypt_alg(), EVP_sha256(), nullptr, password, crypt_strlen(password), PKCS5_DEFAULT_ITER, addrof(ctx->key[0]), addrof(ctx->iv[0])) > 0 ? 0 : -1); } int crypt_strlen(cryptstr_const_t str) { cryptstr_const_t ptr = str; while (*ptr != '\0') ptr++; if ((ptr - str) > INT_MAX) throw std::overflow_error("integer maximum exceeded"); return static_cast(ptr - str); } swirc-3.5.5/src/crypt.h000066400000000000000000000015501501213070300147450ustar00rootroot00000000000000#ifndef CRYPT_H #define CRYPT_H #include #include typedef const unsigned char *cryptstr_const_t; typedef unsigned char *cryptstr_t; typedef unsigned char cryptarray_t[]; typedef struct tagCRYPT_CTX { unsigned char key[EVP_MAX_KEY_LENGTH]; unsigned char iv[EVP_MAX_IV_LENGTH]; } CRYPT_CTX, *PCRYPT_CTX; /*lint -sem(crypt_decrypt_str, r_null) */ /*lint -sem(crypt_encrypt_str, r_null) */ __SWIRC_BEGIN_DECLS STRING crypt_decrypt_str(CSTRING, cryptstr_const_t, const bool); STRING crypt_encrypt_str(cryptstr_const_t, cryptstr_const_t, const bool); void crypt_freezero(void *, const size_t); int crypt_get_base64_decode_length(CSTRING); uint32_t crypt_get_base64_encode_length(const uint32_t); int crypt_get_key_and_iv(cryptstr_const_t password, PCRYPT_CTX); int crypt_strlen(cryptstr_const_t) NONNULL; __SWIRC_END_DECLS #endif swirc-3.5.5/src/curses-funcs.c000066400000000000000000000051731501213070300162240ustar00rootroot00000000000000/* Additional functions for the Ncurses library Copyright (C) 2012-2022 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "curses-funcs.h" bool g_cursesMode = false; PTR_TO_ENDWIN g_endwin_fn = NULL; PTR_TO_DOUPDATE g_doupdate_fn = NULL; void escape_curses(void) { if (g_cursesMode && g_endwin_fn != NULL && g_endwin_fn() != ERR) g_cursesMode = false; } void resume_curses(void) { if (!g_cursesMode && g_doupdate_fn != NULL && g_doupdate_fn() != ERR) g_cursesMode = true; } #if defined(WIN32) && defined(PDC_EXP_EXTRAS) bool is_cleared(const WINDOW *win) { return (win != NULL && win->_clear); } #if PDC_BUILD < 3900 bool is_leaveok(const WINDOW *win) { return (win != NULL && win->_leaveit); } #endif bool is_scrollok(const WINDOW *win) { return (win != NULL && win->_scroll); } bool is_nodelay(const WINDOW *win) { return (win != NULL && win->_nodelay); } bool is_immedok(const WINDOW *win) { return (win != NULL && win->_immed); } bool is_syncok(const WINDOW *win) { return (win != NULL && win->_sync); } #if PDC_BUILD < 3900 bool is_keypad(const WINDOW *win) { return (win != NULL && win->_use_keypad); } #endif #endif swirc-3.5.5/src/curses-funcs.h000066400000000000000000000015211501213070300162220ustar00rootroot00000000000000#ifndef CURSES_FUNCS_H #define CURSES_FUNCS_H #if defined(CURSES_HDR) #include CURSES_HDR #elif UNIX #include #elif WIN32 #include "pdcurses/curses.h" #else #error Cannot determine curses header file! #endif typedef int (*PTR_TO_ENDWIN)(void); typedef int (*PTR_TO_DOUPDATE)(void); extern bool g_cursesMode; extern PTR_TO_ENDWIN g_endwin_fn; extern PTR_TO_DOUPDATE g_doupdate_fn; __SWIRC_BEGIN_DECLS void escape_curses(void); void resume_curses(void); #if defined(WIN32) && defined(PDC_EXP_EXTRAS) bool is_cleared (const WINDOW *); #if PDC_BUILD < 3900 bool is_leaveok (const WINDOW *); #endif bool is_scrollok (const WINDOW *); bool is_nodelay (const WINDOW *); bool is_immedok (const WINDOW *); bool is_syncok (const WINDOW *); #if PDC_BUILD < 3900 bool is_keypad (const WINDOW *); #endif #endif __SWIRC_END_DECLS #endif swirc-3.5.5/src/cursesInit.c000066400000000000000000000156101501213070300157310ustar00rootroot00000000000000/* Initialization of the Ncurses library Copyright (C) 2012-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "curses-funcs.h" #include "cursesInit.h" #include "errHand.h" #include "initcolors.h" #include "strHand.h" #include "theme.h" bool g_no_colors = false; short int g_initialized_pairs = -1; static colorarray_t colors = { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE, #if 0 LIGHT_RED, LIGHT_GREEN, LIGHT_CYAN, LIGHT_BLUE, PINK, #endif GREY, LIGHT_GREY, }; static colorarray_t ext_colors = { 52, 94, 100, 58, 22, 29, 23, 24, 17, 54, 53, 89, /* 16-27 */ 88, 130, 142, 64, 28, 35, 30, 25, 18, 91, 90, 125, /* 28-39 */ 124, 166, 184, 106, 34, 49, 37, 33, 19, 129, 127, 161, /* 40-51 */ 196, 208, 226, 154, 46, 86, 51, 75, 21, 171, 201, 198, /* 52-63 */ 203, 215, 227, 191, 83, 122, 87, 111, 63, 177, 207, 205, /* 64-75 */ 217, 223, 229, 193, 157, 158, 159, 153, 147, 183, 219, 212, /* 76-87 */ 16, 233, 235, 237, 239, 241, 244, 247, 250, 254, 231 /* 88-98 */ }; static const size_t numColors = ARRAY_SIZE(colors); static const size_t numExtended = ARRAY_SIZE(ext_colors); static inline size_t get_num_colors(void) { return (COLORS >= 16 && can_change_color() ? numColors : 8); } static int init_fg_on_bg_case1(short int *pair_n) { #if BSD || LINUX || OS_X FOREACH_FOREGROUND_EXTENDED() { FOREACH_BACKGROUND_ANSI() { if (init_pair(++ (*pair_n), *fg, *bg) == ERR) return ERR; } } #else (void) pair_n; #endif return OK; } static int init_fg_on_bg_case2(short int *pair_n) { #if BSD || LINUX || OS_X FOREACH_FOREGROUND_ANSI() { FOREACH_BACKGROUND_EXTENDED() { if (init_pair(++ (*pair_n), *fg, *bg) == ERR) return ERR; } } #else (void) pair_n; #endif return OK; } static int init_extended_colors(short int *pair_n) { #if BSD || LINUX || OS_X FOREACH_FOREGROUND_EXTENDED() { FOREACH_BACKGROUND_EXTENDED() { if (*fg != *bg && init_pair(++ (*pair_n), *fg, *bg) == ERR) return ERR; } } #else (void) pair_n; #endif return OK; } static int init_more_pairs(short int *pair_n) { if (theme_bool("term_use_default_colors", true)) { for (const short int *psi = &ext_colors[0]; psi < &ext_colors[numExtended]; psi++) { if (init_pair(++ (*pair_n), *psi, -1) == ERR) return ERR; } } if (init_fg_on_bg_case1(pair_n) == ERR || init_fg_on_bg_case2(pair_n) == ERR) return ERR; return init_extended_colors(pair_n); } /** * Initialize color pairs by calling init_pair() * * @return The no. of pairs that have been initialized. And on error * it returns a negative value. */ static short int init_color_pairs(void) { short int pair_n = 0; if (COLORS >= 16 && can_change_color()) { if (init_color(GREY, 498,498,498) == ERR) { err_log(0, "init_color_pairs: init_color: " "GREY error"); } if (init_color(LIGHT_GREY, 824,824,824) == ERR) { err_log(0, "init_color_pairs: init_color: " "LIGHT_GREY error"); } initcolors(); } /* * Initialize black on black */ if (init_pair(++pair_n, colors[0], colors[0]) == ERR) { err_msg("Could not initialize pair %hd", pair_n); return -1; } /* * Initialize a color on the default background of the terminal */ if (theme_bool("term_use_default_colors", true)) { for (const short int *psi = &colors[0]; psi < &colors[get_num_colors()]; psi++) { if (init_pair(++pair_n, *psi, -1) == ERR) { err_msg("Could not initialize pair %hd", pair_n); return -1; } } } FOREACH_FOREGROUND_ANSI() { FOREACH_BACKGROUND_ANSI() { if (*fg != *bg && init_pair(++pair_n, *fg, *bg) == ERR) { if (pair_n == 64) { /* The pair number is 64. The * terminal that is being used * most likely lack support * for pairs >= 64. However: * don't return -1 to indicate * an error. */ return 63; } else { char *fg_name, *bg_name; fg_name = sw_strdup(strColor(*fg)); bg_name = sw_strdup(strColor(*bg)); err_msg("Could not initialize pair %hd " "(%s, %s)", pair_n, fg_name, bg_name); free(fg_name); free(bg_name); return -1; } } } } for (size_t n = 1; n < numColors; n++) { if (init_pair(++pair_n, colors[n], colors[n]) == ERR) return (pair_n - 1); } if (COLORS >= 256) { #if UNIX if (init_more_pairs(&pair_n) == ERR) return (pair_n - 1); #elif WIN32 FOREACH_FOREGROUND_EXTENDED() { if (init_pair(++pair_n, *fg, COLOR_BLACK) == ERR) return (pair_n - 1); } #endif } debug("init_color_pairs: all ok: %hd initialized pairs", pair_n); return pair_n; } /** * Initialization of the Ncurses library (done before usage) */ int curses_init(void) { (void) wrefresh(initscr()); g_cursesMode = true; g_endwin_fn = endwin; g_doupdate_fn = doupdate; if (!theme_bool("term_enable_colors", true) || !has_colors() || start_color() == ERR) { g_no_colors = true; } else { if (theme_bool("term_use_default_colors", true) && use_default_colors() != OK) { err_msg("use_default_colors() ran unsuccessful!\n" "Troubleshooting: " "set option term_use_default_colors to NO."); return ERR; } } if (cbreak() == ERR) { err_msg("Could not enter terminal cbreak mode"); return ERR; } if (noecho() == ERR) { err_msg("Unable to turn echoing off!"); return ERR; } if (!g_no_colors && (g_initialized_pairs = init_color_pairs()) < 0) return ERR; return OK; } swirc-3.5.5/src/cursesInit.h000066400000000000000000000016711501213070300157400ustar00rootroot00000000000000#ifndef CURSES_INIT_H #define CURSES_INIT_H #define LIGHT_RED 8 #define LIGHT_GREEN 9 #define LIGHT_CYAN 11 #define LIGHT_BLUE 12 #define PINK 13 #define GREY 0xff #define LIGHT_GREY 15 #define FOREACH_FOREGROUND_EXTENDED()\ for (const short int *fg = &ext_colors[0]; fg < &ext_colors[numExtended]; fg++) #define FOREACH_BACKGROUND_EXTENDED()\ for (const short int *bg = &ext_colors[0]; bg < &ext_colors[numExtended]; bg++) #define FOREACH_FOREGROUND_ANSI()\ for (const short int *fg = &colors[0];\ fg < &colors[COLORS >= 16 && can_change_color() ? numColors : 8];\ fg++) #define FOREACH_BACKGROUND_ANSI()\ for (const short int *bg = &colors[0];\ bg < &colors[COLORS >= 16 && can_change_color() ? numColors : 8];\ bg++) typedef const short int colorarray_t[]; __SWIRC_BEGIN_DECLS extern bool g_no_colors; extern short int g_initialized_pairs; /* returns OK or ERR */ int curses_init(void); __SWIRC_END_DECLS #endif swirc-3.5.5/src/dataClassify.c000066400000000000000000000330721501213070300162120ustar00rootroot00000000000000/* Data classification utilities Copyright (C) 2012-2025 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "dataClassify.h" #include "errHand.h" #include "strHand.h" static const size_t filename_len_max = 80; static const size_t hostname_len_max = 255; static const size_t nickname_len_max = 45; static const size_t real_name_len_max = 60; static const size_t username_len_max = 100; bool is_alphabetic(const char *string) { if (string == NULL || *string == '\0') return false; for (const char *cp = &string[0]; *cp != '\0'; cp++) { if (!sw_isalpha(*cp)) return false; } return true; } /* * Chinese, Japanese and Korean */ bool is_cjk(const wchar_t wc) { static const RANGE array[] = { { 0x2E80, 0x2EFF, "CJK Radicals Supplement" }, { 0x2F00, 0x2FDF, "Kangxi Radicals" }, { 0x3000, 0x303F, "CJK Symbols and Punctuation" }, { 0x30A0, 0x30FF, "Katakana" }, { 0x3100, 0x312F, "Bopomofo" }, { 0x31A0, 0x31BF, "Bopomofo Extended" }, { 0x31C0, 0x31EF, "CJK Strokes" }, { 0x31F0, 0x31FF, "Katakana Phonetic Extensions" }, { 0x3200, 0x32FF, "Enclosed CJK Letters and Months" }, { 0x3300, 0x33FF, "CJK Compatibility" }, { 0x3400, 0x4DBF, "CJK Unified Ideographs Extension A" }, { 0x4E00, 0x9FFF, "CJK Unified Ideographs" }, { 0xA000, 0xA48F, "Yi Syllables" }, { 0xA490, 0xA4CF, "Yi Radicals" }, { 0xF900, 0xFAFF, "CJK Compatibility Ideographs" }, { 0xFE30, 0xFE4F, "CJK Compatibility Forms" }, { 0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B" }, { 0x2A700, 0x2B739, "CJK Unified Ideographs Extension C" }, { 0x2B740, 0x2B81D, "CJK Unified Ideographs Extension D" }, { 0x2B820, 0x2CEA1, "CJK Unified Ideographs Extension E" }, { 0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement" }, }; static const size_t mid = ARRAY_SIZE(array) / 2; if (wc < array[0].start || wc > array[ARRAY_SIZE(array) - 1].stop) return false; for (const RANGE *rp = &array[wc < array[mid].start ? 0 : mid]; rp < &array[ARRAY_SIZE(array)]; rp++) { if (wc >= rp->start && wc <= rp->stop) return true; } return false; } bool is_combined(const wchar_t wc) { static const RANGE array[] = { { 0x0300, 0x036F, "Combining Diacritical Marks" }, { 0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended" }, // stop 0x1ACE? { 0x1DC0, 0x1DFF, "Combining Diacritical Marks Supplement" }, { 0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols" }, // stop 0x20F0? { 0xFE20, 0xFE2F, "Combining Half Marks" }, }; for (const RANGE *rp = &array[0]; rp < &array[ARRAY_SIZE(array)]; rp++) { if (wc >= rp->start && wc <= rp->stop) return true; } return false; } bool is_irc_channel(const char *name) { if (name == NULL || *name == '\0') return false; return (*name == '&' || *name == '#' || *name == '+' || *name == '!'); } bool is_numeric(const char *string) { if (string == NULL || *string == '\0') return false; for (const char *cp = &string[0]; *cp != '\0'; cp++) { if (!sw_isdigit(*cp)) return false; } return true; } bool is_printable(const char *str) { if (str == NULL || *str == '\0') return false; for (const char *cp = str; *cp != '\0'; cp++) { if (!sw_isprint(*cp)) return false; } return true; } bool is_valid_filename(const char *filename) { static const char legal_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789 ()+-._[]"; if (filename == NULL || *filename == '\0' || xstrnlen(filename, filename_len_max + 1) > filename_len_max || strstr(filename, "..") != NULL) return false; for (const char *cp = &filename[0]; *cp != '\0'; cp++) { if (strchr(legal_index, *cp) == NULL) return false; } return true; } bool is_valid_hostname(const char *hostname) { static const char host_chars[] = "abcdefghijklmnopqrstuvwxyz.0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:"; if (hostname == NULL || *hostname == '\0' || xstrnlen(hostname, hostname_len_max + 1) > hostname_len_max) return false; for (const char *cp = &hostname[0]; *cp != '\0'; cp++) { if (strchr(host_chars, *cp) == NULL) return false; } return true; } bool is_valid_nickname(const char *nickname) { static const char legal_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "-[\\]^_`{|}"; if (nickname == NULL || *nickname == '\0' || xstrnlen(nickname, nickname_len_max + 1) > nickname_len_max) return false; for (const char *cp = &nickname[0]; *cp != '\0'; cp++) { if (strchr(legal_index, *cp) == NULL) return false; } return true; } bool is_valid_real_name(const char *real_name) { if (real_name == NULL || *real_name == '\0' || xstrnlen(real_name, real_name_len_max + 1) > real_name_len_max) return false; for (const char *cp = &real_name[0]; *cp != '\0'; cp++) { if (!sw_isprint(*cp)) return false; } return true; } bool is_valid_username(const char *username) { static const char legal_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "$-./[\\]^_`{|}~"; if (username == NULL || *username == '\0' || xstrnlen(username, username_len_max + 1) > username_len_max) return false; for (const char *cp = &username[0]; *cp != '\0'; cp++) { if (strchr(legal_index, *cp) == NULL) return false; } return true; } bool is_whitespace(const char *string) { if (string == NULL || *string == '\0') return false; for (const char *cp = &string[0]; *cp != '\0'; cp++) { if (!sw_isspace(*cp)) return false; } return true; } /* * Determines the number of column positions required to display a * wide character. * * Specifically written to be used in the printtext and readline * modules. * * Test the 'wc' using iswprint() initially. (This is done elsewhere * so we don't want to do it twice...) */ int xwcwidth(const wchar_t wc, const int fwlen) { /* * XXX: Please keep the 'fullwidth' array sorted. */ static const RANGE fullwidth[] = { {0x2614, 0x2614, "Umbrella with rain drops"}, {0x2615, 0x2615, "Hot beverage"}, {0x2648, 0x2653, "Zodiacal symbols"}, {0x267F, 0x267F, "Wheelchair symbol"}, {0x2693, 0x2693, "Anchor"}, {0x26A1, 0x26A1, "High voltage sign"}, {0x26AA, 0x26AB, "Medium white/black circle"}, {0x26BD, 0x26BE, "Sport symbols"}, {0x26C4, 0x26C5, "Weather symbols"}, {0x26CE, 0x26CE, "Ophiuchus (Zodiacal symbol)"}, {0x26D4, 0x26D4, "No entry traffic sign"}, {0x26EA, 0x26EA, "Church (Map symbol)"}, {0x26F2, 0x26F2, "Fountain"}, {0x26F3, 0x26F3, "Flag in hole"}, {0x26F5, 0x26F5, "Sailboat"}, {0x26FA, 0x26FA, "Tent"}, {0x26FD, 0x26FD, "Fuel pump"}, {0x2705, 0x2705, "White heavy check mark"}, {0x274C, 0x274C, "Cross mark"}, {0x274E, 0x274E, "Negative squared cross mark"}, {0x2795, 0x2797, "Heavy variants of arithmetic symbols"}, {0xFF01, 0xFF5E, "Fullwidth ASCII variants"}, {0xFF5F, 0xFF60, "Fullwidth brackets"}, {0xFFE0, 0xFFE6, "Fullwidth symbol variants"}, {0x1F300, 0x1F30C, "Weather, landscape, and sky symbols"}, {0x1F30D, 0x1F310, "Globe symbols"}, {0x1F311, 0x1F320, "Moon, sun, and star symbols"}, {0x1F32D, 0x1F32F, "Food symbols"}, {0x1F330, 0x1F335, "Plant symbols 1"}, {0x1F337, 0x1F344, "Plant symbols 2"}, {0x1F345, 0x1F353, "Fruit and vegetable symbols"}, {0x1F354, 0x1F374, "Food symbols"}, {0x1F375, 0x1F37C, "Beverage symbols"}, {0x1F37E, 0x1F37F, "Beverage and food symbols"}, {0x1F380, 0x1F393, "Celebration symbols"}, {0x1F3A0, 0x1F3AD, "Entertainment symbols"}, {0x1F3AE, 0x1F3B0, "Game symbols 1"}, {0x1F3B2, 0x1F3B4, "Game symbols 2"}, {0x1F3B5, 0x1F3BC, "Musical symbols"}, {0x1F3BD, 0x1F3CA, "Sport symbols 1"}, {0x1F3CF, 0x1F3D3, "Sport symbols 2"}, {0x1F3E0, 0x1F3F0, "Building and map symbols"}, {0x1F3F4, 0x1F3F4, "Waving black flag"}, {0x1F3F8, 0x1F3F9, "Sport symbols"}, {0x1F3FA, 0x1F3FA, "Amphora"}, // {0x1F3FB, 0x1F3FF, "Emoji modifiers"}, {0x1F400, 0x1F42C, "Animal symbols"}, {0x1F42D, 0x1F43D, "Animal faces"}, {0x1F43E, 0x1F43E, "Paw prints"}, {0x1F440, 0x1F440, "Eyes"}, {0x1F442, 0x1F445, "Facial parts symbols"}, {0x1F446, 0x1F450, "Hand symbols"}, {0x1F451, 0x1F463, "Clothing and accessories"}, {0x1F464, 0x1F477, "Portrait and role symbols"}, {0x1F478, 0x1F480, "Fairy tale symbols"}, {0x1F481, 0x1F483, "Role symbols"}, {0x1F484, 0x1F488, "Personal care symbols"}, {0x1F489, 0x1F48A, "Medical symbols"}, {0x1F48B, 0x1F492, "Romance symbols"}, {0x1F493, 0x1F49F, "Heart symbols"}, {0x1F4A0, 0x1F4AD, "Comic style symbols"}, {0x1F4AE, 0x1F4AF, "Japanese school grade symbols"}, {0x1F4B0, 0x1F4B9, "Money symbols"}, {0x1F4BA, 0x1F4DC, "Office symbols"}, {0x1F4DD, 0x1F4F6, "Communication symbols"}, {0x1F4F7, 0x1F4FC, "Audio and video symbols"}, {0x1F4FF, 0x1F4FF, "Prayer beads"}, {0x1F500, 0x1F518, "User interface symbols"}, {0x1F519, 0x1F51D, "Words with arrows"}, {0x1F51E, 0x1F51F, "Enclosed alphanumeric symbols"}, {0x1F520, 0x1F524, "User interface input status symbols"}, {0x1F525, 0x1F52E, "Tool symbols"}, {0x1F52F, 0x1F531, "Miscellaneous symbols"}, {0x1F532, 0x1F539, "Geometric shapes"}, {0x1F53A, 0x1F53D, "User interface symbols"}, {0x1F54B, 0x1F54E, "Religious symbols"}, {0x1F550, 0x1F567, "Clock face symbols"}, {0x1F57A, 0x1F57A, "Man dancing"}, {0x1F595, 0x1F595, "Reversed hand with middle finger extended"}, {0x1F596, 0x1F596, "Raised hand with part between middle and ring fingers"}, {0x1F5A4, 0x1F5A4, "Black heart"}, {0x1F5FB, 0x1F5FF, "Cultural symbols"}, {0x1F600, 0x1F64F, "Emoticons"}, {0x1F680, 0x1F6A4, "Vehicles"}, {0x1F6A5, 0x1F6A8, "Traffic signs"}, {0x1F6A9, 0x1F6C5, "Signage and other symbols"}, {0x1F6CC, 0x1F6CC, "Sleeping accommodation"}, {0x1F6D0, 0x1F6D2, "Signage and other symbols"}, {0x1F6D5, 0x1F6D7, "Map symbols"}, {0x1F6DD, 0x1F6DF, "Miscellaneous symbols"}, {0x1F6EB, 0x1F6EB, "Airplane departure"}, {0x1F6EC, 0x1F6EC, "Airplane arriving"}, {0x1F6F4, 0x1F6FC, "Vehicles"}, {0x1F90C, 0x1F90C, "Pinched fingers"}, {0x1F90D, 0x1F90E, "Colored heart symbols"}, {0x1F90F, 0x1F90F, "Pinching hand"}, {0x1F910, 0x1F917, "Emoticon faces"}, {0x1F918, 0x1F91F, "Hand symbols"}, {0x1F920, 0x1F92F, "Emoticon faces"}, {0x1F930, 0x1F937, "Portrait and role symbols"}, {0x1F938, 0x1F93A, "Sport symbols 1"}, {0x1F93C, 0x1F93F, "Sport symbols 2"}, {0x1F940, 0x1F945, "Miscellaneous symbols 1"}, {0x1F947, 0x1F94F, "Miscellaneous symbols 2"}, {0x1F950, 0x1F96F, "Food symbols"}, {0x1F970, 0x1F97A, "Faces"}, {0x1F97B, 0x1F97F, "Clothing"}, {0x1F980, 0x1F9AD, "Animal symbols"}, {0x1F9AE, 0x1F9AF, "Accessibility symbols"}, // {0x1F9B0, 0x1F9B3, "Emoji components"}, {0x1F9B4, 0x1F9B7, "Body parts"}, {0x1F9B8, 0x1F9B9, "Role symbols"}, {0x1F9BA, 0x1F9BF, "Accessibility symbols"}, {0x1F9C0, 0x1F9CB, "Food symbols"}, {0x1F9CC, 0x1F9CC, "Troll"}, {0x1F9CD, 0x1F9CF, "Portrait and accessibility symbols"}, {0x1F9D0, 0x1F9D8, "Portrait and role symbols"}, {0x1F9D9, 0x1F9DF, "Fantasy beings"}, {0x1F9E0, 0x1F9E6, "Miscellaneous symbols"}, {0x1F9E7, 0x1F9E9, "Activities"}, {0x1F9EA, 0x1F9FF, "Objects"}, }; static const size_t mid = ARRAY_SIZE(fullwidth) / 2; #if TEST_XWCWIDTH #pragma message("warning: test included (not to be used in production)") if (wc == L'\0' && fwlen == 7357) { for (const RANGE *rp = &fullwidth[0]; rp < &fullwidth[ARRAY_SIZE(fullwidth)]; rp++) { if (rp->start > rp->stop) err_quit("%s: test failed", __func__); } } #endif if (wc >= 0x20 && wc <= 0xFF) return 1; else if (wc < 0x20 || is_combined(wc)) return 0; else if (wc < fullwidth[0].start || wc > fullwidth[ARRAY_SIZE(fullwidth) - 1].stop) return (is_cjk(wc) ? fwlen : 1); const size_t begin = (wc < fullwidth[mid].start ? 0 : mid); const size_t end = (wc < fullwidth[mid].start ? mid : ARRAY_SIZE(fullwidth)); for (const RANGE *rp = &fullwidth[begin]; rp < &fullwidth[end]; rp++) { if (wc >= rp->start && wc <= rp->stop) return fwlen; } return (is_cjk(wc) ? fwlen : 1); } int xwcswidth(const wchar_t *str, const int fwlen) { const wchar_t *ptr = str; unsigned int width = 0; while (*ptr) { width += xwcwidth(*ptr, fwlen); ptr++; } return width; } swirc-3.5.5/src/dataClassify.h000066400000000000000000000043721501213070300162200ustar00rootroot00000000000000#ifndef DATA_CLASSIFY_H #define DATA_CLASSIFY_H #include #define TEST_XWCWIDTH 0 typedef struct tagRANGE { #if defined(UNIX) wchar_t start; wchar_t stop; #elif defined(WIN32) uint32_t start; uint32_t stop; #endif CSTRING comment; } RANGE, *PRANGE; __SWIRC_BEGIN_DECLS bool is_alphabetic(const char *); bool is_cjk(const wchar_t); bool is_combined(const wchar_t); bool is_irc_channel(const char *); bool is_numeric(const char *); bool is_printable(const char *); bool is_valid_filename(const char *); bool is_valid_hostname(const char *); bool is_valid_nickname(const char *); bool is_valid_real_name(const char *); bool is_valid_username(const char *); bool is_whitespace(const char *); int xwcwidth(const wchar_t, const int); int xwcswidth(const wchar_t *, const int) NONNULL; __SWIRC_END_DECLS /* Inline function definitions =========================== */ #ifndef _lint static SW_INLINE bool isNull(const void *data) { return (data == NULL); } #else #define isNull(_obj) ((_obj) == NULL) #endif static SW_INLINE bool isEmpty(const char *data) { return (*data == '\0'); } #ifndef _lint static SW_INLINE bool isValid(const void *ptr) { #if defined(HAVE_ETEXT_SEGMENT) extern char etext; return (ptr != NULL && ((const char *) ptr) > &etext); #else return (ptr != NULL); #endif } #else #define isValid(_ptr) ((_ptr) != NULL) #endif #include #include #include /* EOF */ static SW_INLINE int sw_isalnum(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isalnum(c)); } static SW_INLINE int sw_isalpha(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isalpha(c)); } static SW_INLINE int sw_isdigit(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isdigit(c)); } static SW_INLINE int sw_isspace(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isspace(c)); } static SW_INLINE int sw_isprint(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isprint(c)); } static SW_INLINE int sw_isupper(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : isupper(c)); } static SW_INLINE int sw_islower(const int c) { return ((c == EOF || c < 0 || c > UCHAR_MAX) ? false : islower(c)); } #endif /* DATA_CLASSIFY_H */ swirc-3.5.5/src/errHand.c000066400000000000000000000157031501213070300151670ustar00rootroot00000000000000/* errHand.c -- Error handling routines Copyright (C) 2012-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include /* strerror_r() */ #include #ifdef WIN32 #include /* MessageBox() */ #endif #ifdef WIN32 #include "assertAPI.h" #endif #include "curses-funcs.h" #include "errHand.h" #include "libUtils.h" #include "main.h" #include "nestHome.h" #include "strHand.h" static const char * get_timestamp(void) { static char buffer[200] = { '\0' }; struct tm items = { 0 }; time_t seconds = 0; if (time(&seconds) == g_time_error) return ""; #if defined(UNIX) if (localtime_r(&seconds, &items) == NULL) return ""; #elif defined(WIN32) if (localtime_s(&items, &seconds) != 0) return ""; #endif return ((strftime(buffer, sizeof buffer, "%c", &items) > 0) ? &buffer[0] : ""); } static void write_to_error_log(const char *msg) { FILE *fp; char path[1300] = { '\0' }; if (msg == NULL || g_log_dir == NULL || sw_strcpy(path, g_log_dir, sizeof path) != 0) return; #if defined(UNIX) if (sw_strcat(path, "/error.log", sizeof path) != 0) return; #elif defined(WIN32) if (sw_strcat(path, "\\error.log", sizeof path) != 0) return; #endif if ((fp = xfopen(path, "a")) != NULL) { #ifdef HAVE_BCI (void) fprintf_s(fp, "%s %s[%ld]: %s\n", get_timestamp(), g_progname, g_pid, msg); #else (void) fprintf(fp, "%s %s[%ld]: %s\n", get_timestamp(), g_progname, g_pid, msg); #endif (void) fclose(fp); } } static void err_doit(bool output_to_stderr, int error, const char *fmt, va_list ap) { char out[1300] = { '\0' }; char strerrbuf[MAXERROR] = { '\0' }; #if defined(UNIX) (void) vsnprintf(out, sizeof out, fmt, ap); #elif defined(WIN32) (void) vsnprintf_s(out, sizeof out, _TRUNCATE, fmt, ap); #endif if (error) { (void) sw_strcat(out, ": ", sizeof out); (void) sw_strcat(out, xstrerror(error, strerrbuf, MAXERROR), sizeof out); } write_to_error_log(out); if (output_to_stderr) { escape_curses(); (void) fputs(out, stderr); (void) fputc('\n', stderr); #ifdef WIN32 (void) MessageBox(NULL, out, "Fatal", (MB_OK | MB_ICONSTOP | MB_DEFBUTTON1)); #endif } } NORETURN void err_dump(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, errno, fmt, ap); va_end(ap); abort(); } NORETURN void err_exit(int error, const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, error, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } NORETURN void err_quit(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, 0, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } NORETURN void err_sys(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, errno, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } void err_log(int error, const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(false, error, fmt, ap); va_end(ap); } void err_msg(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, 0, fmt, ap); va_end(ap); } void err_ret(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(true, errno, fmt, ap); va_end(ap); } const char * errdesc_by_last_err(void) { #if defined(UNIX) return ("Unknown error!"); #elif defined(WIN32) TCHAR lpBuffer[MAXERROR]; const DWORD dwFlags = (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS); const DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT); const DWORD dwMessageId = GetLastError(); static char desc[MAXERROR]; BZERO(lpBuffer, sizeof lpBuffer); if (sizeof(TCHAR) != 1 || !FormatMessage(dwFlags, NULL, dwMessageId, dwLanguageId, addrof(lpBuffer[0]), ARRAY_SIZE(lpBuffer), NULL)) return ("Unknown error!"); sw_static_assert(sizeof(TCHAR) == 1, "TCHAR unexpectedly large. " "UNICODE defined?"); (void) memcpy(desc, lpBuffer, ARRAY_SIZE(desc)); return addrof(desc[0]); #endif } const char * errdesc_by_num(int num) { static char desc[600] = { '\0' }; #ifdef HAVE_BCI if (num == 0 || strerror_s(desc, sizeof desc, num) != 0) return ("Unknown error!"); #else if (num == 0 || strerror_r(num, desc, sizeof desc) != 0) return ("Unknown error!"); #endif return (trim(desc)); } const char * xstrerror(int errnum, char *strerrbuf, size_t buflen) { #ifdef HAVE_BCI if (errnum == 0 || strerror_s(strerrbuf, buflen, errnum) != 0) return ("Unknown error!"); #else if (errnum == 0 || strerror_r(errnum, strerrbuf, buflen) != 0) return ("Unknown error!"); #endif return (trim(strerrbuf)); } /* ----------------------------------------------------------------- */ static void debug_doit(const char *fmt, va_list ap) { FILE *fp; char out[1300] = { '\0' }; char path[1300] = { '\0' }; #if defined(UNIX) (void) vsnprintf(out, ARRAY_SIZE(out), fmt, ap); #elif defined(WIN32) (void) vsnprintf_s(out, ARRAY_SIZE(out), _TRUNCATE, fmt, ap); #endif if (g_log_dir == NULL || sw_strcpy(path, g_log_dir, ARRAY_SIZE(path)) != 0) return; #if defined(UNIX) if (sw_strcat(path, "/debug.log", ARRAY_SIZE(path)) != 0) return; #elif defined(WIN32) if (sw_strcat(path, "\\debug.log", ARRAY_SIZE(path)) != 0) return; #endif if ((fp = xfopen(path, "a")) != NULL) { #ifdef HAVE_BCI (void) fprintf_s(fp, "%s %s[%ld]: %s\n", get_timestamp(), g_progname, g_pid, out); #else (void) fprintf(fp, "%s %s[%ld]: %s\n", get_timestamp(), g_progname, g_pid, out); #endif (void) fclose(fp); } } void debug(const char *fmt, ...) { if (g_debug_logging) { va_list ap; va_start(ap, fmt); debug_doit(fmt, ap); va_end(ap); } } swirc-3.5.5/src/errHand.h000066400000000000000000000017241501213070300151720ustar00rootroot00000000000000#ifndef SW_ERROR_HANDLING_H #define SW_ERROR_HANDLING_H /*lint -printf(1, err_dump, err_msg, err_quit, err_ret, err_sys) */ /*lint -printf(2, err_exit, err_log) */ /*lint -sem(err_dump, r_no) */ /*lint -sem(err_exit, r_no) */ /*lint -sem(err_quit, r_no) */ /*lint -sem(err_sys, r_no) */ __SWIRC_BEGIN_DECLS NORETURN void err_dump (const char *, ...) PRINTFLIKE(1); NORETURN void err_exit (int error, const char *, ...) PRINTFLIKE(2); NORETURN void err_quit (const char *, ...) PRINTFLIKE(1); NORETURN void err_sys (const char *, ...) PRINTFLIKE(1); void err_log (int error, const char *, ...) PRINTFLIKE(2); void err_msg (const char *, ...) PRINTFLIKE(1); void err_ret (const char *, ...) PRINTFLIKE(1); #define MAXERROR 600 const char *errdesc_by_last_err(void); const char *errdesc_by_num(int); const char *xstrerror(int errnum, char *strerrbuf, size_t buflen); /*lint -printf(1, debug) */ void debug(const char *, ...) PRINTFLIKE(1); __SWIRC_END_DECLS #endif swirc-3.5.5/src/events/000077500000000000000000000000001501213070300147365ustar00rootroot00000000000000swirc-3.5.5/src/events/README.md000066400000000000000000000001401501213070300162100ustar00rootroot00000000000000# README # The files within this folder contains source code of files that handles IRC events. swirc-3.5.5/src/events/account.cpp000066400000000000000000000101331501213070300170740ustar00rootroot00000000000000/* events/account.cpp Copyright (C) 2018-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "../dataClassify.h" #include "../irc.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "account.h" #include "i18n.h" #include "names.h" static void print_logged_in(PPRINTTEXT_CONTEXT, const char *, const char *, const char *, const char *) NONNULL; static void print_logged_out(PPRINTTEXT_CONTEXT, const char *, const char *, const char *) NONNULL; static void print_logged_in(PPRINTTEXT_CONTEXT ctx, const char *nick, const char *user, const char *host, const char *accountname) { printtext(ctx, _("%s%s%c %s%s@%s%s has " "logged into a new account %s%s%c"), COLOR1, nick, NORMAL, LEFT_BRKT, user, host, RIGHT_BRKT, COLOR4, accountname, NORMAL); } static void print_logged_out(PPRINTTEXT_CONTEXT ctx, const char *nick, const char *user, const char *host) { printtext(ctx, _("%s%s%c %s%s@%s%s has " "logged out of their account"), COLOR2, nick, NORMAL, LEFT_BRKT, user, host, RIGHT_BRKT); } /* event_account Examples: :nick!user@host ACCOUNT (logged into a new account) :nick!user@host ACCOUNT * (logged out of their account) */ void event_account(struct irc_message_compo *compo) { PRINTTEXT_CONTEXT ctx; try { char *last = const_cast(""); char *prefix; const char *accountname; const char *nick, *user, *host; if ((prefix = compo->prefix) == NULL) throw std::runtime_error("no prefix"); else prefix++; if ((nick = strtok_r(prefix, "!@", &last)) == NULL || (user = strtok_r(NULL, "!@", &last)) == NULL || (host = strtok_r(NULL, "!@", &last)) == NULL) throw std::runtime_error("no nick or user@host"); else if (*(accountname = compo->params) == ':') accountname++; const bool logged_out = strings_match(accountname, "*"); printtext_context_init(&ctx, NULL, TYPE_SPEC1_SPEC2, true); for (int i = 1; i <= g_ntotal_windows; i++) { PIRC_WINDOW window; if ((window = window_by_refnum(i)) != NULL && is_irc_channel(window->label) && event_names_htbl_lookup(nick, window->label) != NULL) { ctx.window = window; if (logged_out) { print_logged_out(&ctx, nick, user, host); } else { /* * Logged in... */ print_logged_in(&ctx, nick, user, host, accountname); } } } /* for */ } catch (const std::runtime_error &e) { printtext_context_init(&ctx, g_status_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "%s: %s", __func__, e.what()); } } swirc-3.5.5/src/events/account.h000066400000000000000000000002031501213070300165360ustar00rootroot00000000000000#ifndef ACCOUNT_H #define ACCOUNT_H __SWIRC_BEGIN_DECLS void event_account(struct irc_message_compo *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/events/auth.c000066400000000000000000000154421501213070300160510ustar00rootroot00000000000000/* events/auth.c Copyright (C) 2017-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "base64.h" #include "common.h" #include "../config.h" #include "../crypt.h" #include "../errHand.h" #include "../irc.h" #include "../libUtils.h" #include "../network.h" #include "../printtext.h" #include "../strHand.h" #include "../strdup_printf.h" #include "../commands/sasl-scram-sha.h" #include "../commands/sasl.h" #include "auth.h" #include "cap.h" /* get_sasl_mechanism() */ static void abort_authentication(void) { (void) net_send("AUTHENTICATE *"); (void) net_send("CAP END"); } /*lint -sem(get_b64_encoded_username, r_null) */ static char * get_b64_encoded_username(void) { char *encoded_username; const char *username = Config("sasl_username"); const size_t namesize = 500; if (strings_match(username, "")) return NULL; encoded_username = xmalloc(namesize + 1); encoded_username[namesize] = '\0'; if (b64_encode((uint8_t *) username, strlen(username), encoded_username, namesize) == -1) { free(encoded_username); return NULL; } return encoded_username; } static bool build_auth_message(char **msg) { char *msg_unencoded; const char *username = Config("sasl_username"); const char *password = Config("sasl_password"); const size_t msgsize = 1000; size_t len; if (strings_match(username, "") || strings_match(password, "")) { *msg = NULL; return false; } msg_unencoded = strdup_printf("%s%c%s%c%s", username, '\0', username, '\0', password); len = size_product(strlen(username), 2); len += 2; len += strlen(password); *msg = xmalloc(msgsize + 1); (*msg)[msgsize] = '\0'; if (b64_encode((uint8_t *) msg_unencoded, len, *msg, msgsize) == -1) { free(msg_unencoded); free(*msg); *msg = NULL; return false; } free(msg_unencoded); return true; } static void handle_ecdsa_nist256p_challenge(const char *challenge) { char *err_reason = NULL; char *solution; if ((solution = solve_ecdsa_nist256p_challenge(challenge, &err_reason)) == NULL) { err_log(0, "solve_ecdsa_nist256p_challenge: %s", err_reason); free(err_reason); abort_authentication(); return; } (void) net_send("AUTHENTICATE %s", solution); free(solution); } static void handle_else_branch(const char *mechanism, const char *params) { if (strings_match(mechanism, "ECDSA-NIST256P-CHALLENGE")) { handle_ecdsa_nist256p_challenge(params); } else if (strings_match(mechanism, "EXTERNAL")) { err_log(EPROTO, "%s: error (external): expected '+'", __func__); abort_authentication(); } else if (strings_match(mechanism, "PLAIN")) { err_log(EPROTO, "%s: error (plain): expected '+'", __func__); abort_authentication(); } else if (strings_match(mechanism, "SCRAM-SHA-1") || strings_match(mechanism, "SCRAM-SHA-256") || strings_match(mechanism, "SCRAM-SHA-512")) { if (!g_sasl_scram_sha_got_first_msg) { if (sasl_scram_sha_handle_serv_first_msg(params) == -1) abort_authentication(); else g_sasl_scram_sha_got_first_msg = true; } else { if (sasl_scram_sha_handle_serv_final_msg(params) == -1) abort_authentication(); else (void) net_send("AUTHENTICATE +"); } } else { err_log(0, "%s: unknown mechanism", __func__); err_log(0, "%s: aborting authentication...", __func__); abort_authentication(); } } void event_authenticate(struct irc_message_compo *compo) { const char *mechanism = get_sasl_mechanism(); if (strings_match(compo->params, "+")) { if (strings_match(mechanism, "ECDSA-NIST256P-CHALLENGE")) { char *encoded_username; if ((encoded_username = get_b64_encoded_username()) == NULL) { abort_authentication(); return; } (void) net_send("AUTHENTICATE %s", encoded_username); free(encoded_username); } else if (strings_match(mechanism, "EXTERNAL")) { (void) net_send("AUTHENTICATE +"); } else if (strings_match(mechanism, "PLAIN")) { char *msg = NULL; if (!build_auth_message(&msg)) { abort_authentication(); return; } (void) net_send("AUTHENTICATE %s", msg); crypt_freezero(msg, xstrnlen(msg, 1000)); } else if (strings_match(mechanism, "SCRAM-SHA-1") || strings_match(mechanism, "SCRAM-SHA-256") || strings_match(mechanism, "SCRAM-SHA-512")) { if (sasl_scram_sha_send_client_first_msg() == -1) abort_authentication(); } else { err_log(0, "SASL mechanism unknown -- " "aborting authentication!"); abort_authentication(); return; } } else { /* * not 'AUTHENTICATE +'... */ handle_else_branch(mechanism, compo->params); } } /* Examples: :server 902 :You must use a nick assigned to you :server 904 :SASL authentication failed :server 905 :SASL message too long :server 907 :You have already authenticated using SASL :server 908 :are available SASL mechanisms */ void handle_sasl_auth_fail(struct irc_message_compo *compo) { if (strings_match(compo->command, "908")) squeeze(compo->params, ":"); irc_extract_msg(compo, g_status_window, 1, true); abort_authentication(); } /* sasl_auth_success: 903 (RPL_SASLSUCCESS) Example: :server 903 :SASL authentication successful */ void sasl_auth_success(struct irc_message_compo *compo) { PRINTTEXT_CONTEXT ctx; (void) compo; printtext_context_init(&ctx, g_status_window, TYPE_SPEC1_SUCCESS, true); printtext(&ctx, "SASL authentication successful"); (void) net_send("CAP END"); } swirc-3.5.5/src/events/auth.h000066400000000000000000000003561501213070300160540ustar00rootroot00000000000000#ifndef AUTH_H #define AUTH_H __SWIRC_BEGIN_DECLS void event_authenticate(struct irc_message_compo *); void handle_sasl_auth_fail(struct irc_message_compo *); void sasl_auth_success(struct irc_message_compo *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/events/away.cpp000066400000000000000000000103461501213070300164070ustar00rootroot00000000000000/* events/away.cpp Copyright (C) 2018-2023 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include "../strHand.h" #include "../dataClassify.h" #include "../irc.h" #include "../printtext.h" #include "../theme.h" #include "away.h" #include "i18n.h" #include "names.h" static void print_unaway(PPRINTTEXT_CONTEXT, const char *, const char *, const char *) NONNULL; static void print_nowAway(PPRINTTEXT_CONTEXT, const char *, const char *, const char *, const char *) NONNULL; static void print_unaway(PPRINTTEXT_CONTEXT ctx, const char *nick, const char *user, const char *host) { printtext(ctx, _("%s%s%c %s%s@%s%s is no longer marked " "as being away!"), COLOR1, nick, NORMAL, LEFT_BRKT, user, host, RIGHT_BRKT); } static void print_nowAway(PPRINTTEXT_CONTEXT ctx, const char *nick, const char *user, const char *host, const char *message) { printtext(ctx, _("%s%s%c %s%s@%s%s has been marked " "as being away (%s)"), COLOR2, nick, NORMAL, LEFT_BRKT, user, host, RIGHT_BRKT, message); } /* event_away Example: :nick!user@host AWAY [message] */ void event_away(struct irc_message_compo *compo) { PRINTTEXT_CONTEXT ctx; try { char *last = const_cast(""); char *prefix, *message; const char *nick, *user, *host; if (compo == NULL) throw std::runtime_error("no components"); else if ((prefix = compo->prefix) == NULL) throw std::runtime_error("null prefix"); else prefix++; if ((message = compo->params) != NULL) { if (*message == ':') message++; (void) squeeze_text_deco(message); } if ((nick = strtok_r(prefix, "!@", &last)) == NULL || (user = strtok_r(NULL, "!@", &last)) == NULL || (host = strtok_r(NULL, "!@", &last)) == NULL) throw std::runtime_error("no nick or user@host"); printtext_context_init(&ctx, NULL, TYPE_SPEC1_SPEC2, true); for (int i = 1; i <= g_ntotal_windows; i++) { PIRC_WINDOW window; if ((window = window_by_refnum(i)) != NULL && is_irc_channel(window->label) && event_names_htbl_lookup(nick, window->label) != NULL) { ctx.window = window; if (message != NULL) { print_nowAway(&ctx, nick, user, host, message); } else { print_unaway(&ctx, nick, user, host); } } } /* for */ } catch (const std::runtime_error &e) { printtext_context_init(&ctx, g_status_window, TYPE_SPEC1_FAILURE, true); printtext(&ctx, "%s(%s): %s", __func__, compo->command, e.what()); } } /* * 305 RPL_UNAWAY */ void event_unaway(struct irc_message_compo *compo) { irc_extract_msg(compo, g_active_window, 1, false); } /* * 306 RPL_NOWAWAY */ void event_nowAway(struct irc_message_compo *compo) { irc_extract_msg(compo, g_active_window, 1, false); } swirc-3.5.5/src/events/away.h000066400000000000000000000003311501213070300160450ustar00rootroot00000000000000#ifndef AWAY_H #define AWAY_H __SWIRC_BEGIN_DECLS void event_away(struct irc_message_compo *); void event_unaway(struct irc_message_compo *); void event_nowAway(struct irc_message_compo *); __SWIRC_END_DECLS #endif swirc-3.5.5/src/events/banlist.cpp000066400000000000000000000204421501213070300171000ustar00rootroot00000000000000/* Event 367 (RPL_BANLIST) and 368 (RPL_ENDOFBANLIST) Copyright (C) 2016-2024 Markus Uhlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include #include #include #include "../dataClassify.h" #include "../errHand.h" #include "../irc.h" #include "../printtext.h" #include "../strHand.h" #include "../theme.h" #include "banlist.h" #define TM_STRUCT_MSG "unable to retrieve tm structure" static std::string get_channel(const char *channel) { std::string str(LEFT_BRKT); (void) str.append(COLOR1).append(channel).append(TXT_NORMAL); (void) str.append(RIGHT_BRKT); return str; } static std::string get_mask(const char *mask) { std::string str(""); (void) str.append(COLOR4).append(mask).append(TXT_NORMAL); return str; } static void feeds_written_4(PPRINTTEXT_CONTEXT ctx, char *params) { char *channel, *mask, *issuer, *seconds; char *issuer_name, *issuer_userhost; char *state1 = const_cast(""); char *state2 = const_cast(""); char buf[500] = { '\0' }; char tbuf[100] = { '\0' }; struct tm result = { 0 }; /* recipient */ (void) strtok_r(params, "\n", &state1); if ((channel = strtok_r(NULL, "\n", &state1)) == NULL) throw std::runtime_error("unable to get channel"); else if ((mask = strtok_r(NULL, "\n", &state1)) == NULL) throw std::runtime_error("unable to get mask"); else if ((issuer = strtok_r(NULL, "\n", &state1)) == NULL) throw std::runtime_error("unable to get issuer"); else if ((seconds = strtok_r(NULL, "\n", &state1)) == NULL) throw std::runtime_error("unable to get seconds"); else if (sw_strcpy(buf, issuer, ARRAY_SIZE(buf)) != 0) throw std::runtime_error("cannot copy issuer"); else if (!is_numeric(seconds)) throw std::runtime_error("seconds not a number"); if ((ctx->window = window_by_label(channel)) == NULL) ctx->window = g_status_window; if ((issuer_name = strtok_r(buf, "!", &state2)) == NULL) throw std::runtime_error("unable to get issuer name"); if ((issuer_userhost = strtok_r(NULL, "!", &state2)) == NULL) issuer_userhost = const_cast(""); const time_t date_of_issue = static_cast(strtol(seconds, NULL, 10)); #if defined(UNIX) if (localtime_r(&date_of_issue, &result) == NULL) throw std::runtime_error("localtime_r: " TM_STRUCT_MSG); #elif defined(WIN32) if (localtime_s(&result, &date_of_issue) != 0) throw std::runtime_error("localtime_s: " TM_STRUCT_MSG); #endif if (strftime(tbuf, ARRAY_SIZE(tbuf), "%c", &result) == 0) throw std::runtime_error("strftime: zero return"); printtext(ctx, "%s: %s issued by %s%s%s %s%s%s %s%s%s", get_channel(channel).c_str(), get_mask(mask).c_str(), COLOR2, issuer_name, TXT_NORMAL, LEFT_BRKT, issuer_userhost, RIGHT_BRKT, LEFT_BRKT, trim(tbuf), RIGHT_BRKT); } static void feeds_written_2(PPRINTTEXT_CONTEXT ctx, char *params) { char *channel, *mask; char *last = const_cast(""); (void) strtok_r(params, "\n", &last); if ((channel = strtok_r(NULL, "\n", &last)) == NULL) throw std::runtime_error("unable to get channel"); else if ((mask = strtok_r(NULL, "\n", &last)) == NULL) throw std::runtime_error("unable to get mask"); if ((ctx->window = window_by_label(channel)) == NULL) ctx->window = g_status_window; printtext(ctx, "%s: %s", get_channel(channel).c_str(), get_mask(mask).c_str()); } /* event_banlist: 367 Examples: :irc.server.com 367 irc.server.com