m (Polsaker moved page Dunes.p.mojavenet.xyz to Server:dunes.p.mojavenet.xyz: New namespace) |
No edit summary |
||
(6 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{InfoboxNode | {{InfoboxNode | ||
|owner=clrx | |||
|status=Delinked | |||
|delink_reason=Missing for 9 weeks, can relink | |||
|sid=8SL | |||
|spkifp=F5/EHulkNjcBbZsHg8BAfrZ4CUWKc/DyhsRsmSleugk= | |||
|serverport=6900 | |||
|noincoming=No | |||
|region=America | |||
|location=San Jose, California, USA | |||
|provider=Oracle | |||
|type=Leaf | |||
|image=Larry.png | |image=Larry.png | ||
|name=dunes.p.mojavenet.xyz | |name=dunes.p.mojavenet.xyz | ||
}} | }} | ||
ports: 6667, 6697 (SSL) | ports: 6667, 6697 (SSL) | ||
IPv4 & IPv6! | IPv4 & IPv6! | ||
Line 16: | Line 21: | ||
Create a new #channel, get a free +q! | Create a new #channel, get a free +q! | ||
[[File:Larry.png|thumb]] | |||
Line 27: | Line 33: | ||
===Modules=== | ===Modules=== | ||
{| class="wikitable" | |||
|+ | |||
!Name | |||
!Function | |||
|- | |||
|certificate-refresh | |||
|Automatically reload SSL/TLS certificate when the underlying certificate on the filesystem has changed. | |||
|- | |||
|hyb-statsconn | |||
|RPL_STATSCONN to new clients and LUSERS, like Hybrid/Ratbox | |||
|- | |||
|no-services-plus-q | |||
|Channel creator is automatically given +q/~ | |||
|} | |||
====/src/modules/third/certificate-refresh.c==== | ====/src/modules/third/certificate-refresh.c==== | ||
Line 42: | Line 62: | ||
"1.0.0", /* version */ | "1.0.0", /* version */ | ||
"Certificate Refresh: Automatically checks SSL/TLS certificates for changes, and reloads SSL/TLS if the certificate has been updated.", /* description */ | "Certificate Refresh: Automatically checks SSL/TLS certificates for changes, and reloads SSL/TLS if the certificate has been updated.", /* description */ | ||
" | "clrx", /* author */ | ||
"unrealircd-5", /* do not change this, it indicates module API version */ | "unrealircd-5", /* do not change this, it indicates module API version */ | ||
}; | }; | ||
Line 67: | Line 87: | ||
*/ | */ | ||
#define _MCRF_USE_REINIT_SSL (UNREAL_VERSION_GENERATION == 5 && UNREAL_VERSION_MAJOR == 2 && UNREAL_VERSION_MINOR < 1) | |||
#define _MCRF_TIMEOUT 43200000 /* 12 hours, in milliseconds. */ | #define _MCRF_TIMEOUT 43200000 /* 12 hours, in milliseconds. */ | ||
typedef struct mCRF_Watch_File mCRF_Watch_File; | typedef struct mCRF_Watch_File mCRF_Watch_File; | ||
struct mCRF_Watch_File { | struct mCRF_Watch_File | ||
{ | |||
char *path; | char *path; | ||
time_t last_modified; | time_t last_modified; | ||
Line 105: | Line 127: | ||
{ | { | ||
sendto_snomask(SNO_SNOTICE, "*** [third/certificate-refresh]: Server TLS certificate(s) updated, reloading certificates."); | sendto_snomask(SNO_SNOTICE, "*** [third/certificate-refresh]: Server TLS certificate(s) updated, reloading certificates."); | ||
#if _MCRF_USE_REINIT_SSL | |||
reinit_ssl(NULL); | reinit_ssl(NULL); | ||
#else | |||
reinit_tls(); | |||
#endif | |||
} | } | ||
Line 114: | Line 141: | ||
ConfigItem_listen *lptr; | ConfigItem_listen *lptr; | ||
ConfigItem_sni *sptr; | ConfigItem_sni *sptr; | ||
TLSOptions *tls_options = get_tls_options_for_client(&me); | TLSOptions *tls_options = get_tls_options_for_client(&me); | ||
Line 224: | Line 250: | ||
{ | { | ||
return MOD_SUCCESS; | return MOD_SUCCESS; | ||
} | |||
</syntaxhighlight>'''/src/modules/third/hyb-statsconn.c''' | |||
Shows RPL_STATSCONN upon client connection and when LUSERS is called. Doesn't add a lot of value otherwise, but I like how it shows how many actual IRC clients (as opposed to all accept()s in /STATS T) have connected to the server during the current run. I named it hybrid-statsconn since that is the first time I remember seeing this information, but I don't know where this numeric really came from. Oh well :) | |||
[22:56:45] * There are 6 users and 410 invisible on 147 servers | |||
[22:56:45] * 129 operator(s) online | |||
[22:56:45] * 218 channels formed | |||
[22:56:45] * I have 4 clients and 1 servers | |||
[22:56:45] * Current local users 4, max 6 | |||
[22:56:45] * Current global users 416, max 1000001 | |||
[22:56:45] * Highest connection count: 7 (6 clients) (26 connections received) | |||
(These were the stats after running for about two weeks without a reboot in the round robin) | |||
<syntaxhighlight lang="c"> | |||
/* License: GPLv2 | |||
* Author: clrx | |||
*/ | |||
#include "unrealircd.h" | |||
ModuleHeader MOD_HEADER | |||
= { | |||
"third/hyb-statsconn", /* name */ | |||
"1.0.0", /* version */ | |||
"Hyb-StatsConn: Shows RPL_STATSCONN", /* description */ | |||
"clrx", /* author */ | |||
"unrealircd-5", /* do not change this, it indicates module API version */ | |||
}; | |||
#define RPL_STATSCONN_MESSAGE ":Highest connection count: %d (%d clients) (%ld connections received)" | |||
int m_hyb_statsconn_hook_welcome(Client *acptr, int after_numeric); | |||
int m_hyb_statsconn_hook_local_server_connect(Client *acptr); | |||
int m_hyb_statsconn_rehash_complete(void); | |||
CMD_OVERRIDE_FUNC(m_hyb_statsconn_override); | |||
void m_hyb_statsconn_show_statsconn(Client *acptr); | |||
static long totalrestartcount = 0; | |||
/* Keep track of our handle in order to reinstall the override after each rehash. */ | |||
Module *myHandle; | |||
MOD_TEST() | |||
{ | |||
return MOD_SUCCESS; | |||
} | |||
MOD_INIT() | |||
{ | |||
/* Must mark as permanent, or else we will lose our count. */ | |||
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); | |||
myHandle = modinfo->handle; | |||
HookAdd(modinfo->handle, HOOKTYPE_SERVER_CONNECT, 0, m_hyb_statsconn_hook_local_server_connect); | |||
HookAdd(modinfo->handle, HOOKTYPE_WELCOME, 0, m_hyb_statsconn_hook_welcome); | |||
/* Even if the module is marked as permanent, /REHASH will unload the CommandOverride. So, if we rehash, we need to reload the Override. */ | |||
HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 999, m_hyb_statsconn_rehash_complete); | |||
return MOD_SUCCESS; | |||
} | |||
MOD_LOAD() | |||
{ | |||
/* Install override on first startup, only run once due to permanent module. */ | |||
m_hyb_statsconn_rehash_complete(); | |||
return MOD_SUCCESS; | |||
} | |||
MOD_UNLOAD() | |||
{ | |||
return MOD_SUCCESS; | |||
} | |||
CMD_OVERRIDE_FUNC(m_hyb_statsconn_override) | |||
{ | |||
CallCommandOverride(ovr, client, recv_mtags, parc, parv); | |||
/* This is recommended per the Unreal wiki. It's probably unnecessary as at this time | |||
* lusers does not offer a way to kill the client, and sendbufto_one also checks for a | |||
* dead socket. But it doesn't hurt anything and provides future proofing incase | |||
* other innovations are invented! | |||
*/ | |||
if (IsDead(client)) | |||
return; | |||
if (parc > 1) | |||
{ | |||
Client *target = find_server(parv[1], NULL); | |||
if (target != &me) | |||
return; | |||
} | |||
m_hyb_statsconn_show_statsconn(client); | |||
} | |||
int m_hyb_statsconn_rehash_complete(void) | |||
{ | |||
/* re-install override after each rehash */ | |||
CommandOverrideAdd(myHandle, "LUSERS", m_hyb_statsconn_override); | |||
return HOOK_CONTINUE; | |||
} | |||
int m_hyb_statsconn_hook_local_server_connect(Client *acptr) | |||
{ | |||
/* Only care about local clients, but hooked in for all servers per API. Only increment | |||
* if client is a local server. */ | |||
if (MyConnect(acptr) && IsServer(acptr)) | |||
totalrestartcount = totalrestartcount + 1; | |||
return HOOK_CONTINUE; | |||
} | |||
int m_hyb_statsconn_hook_welcome(Client *acptr, int after_numeric) | |||
{ | |||
/* Is HOOK_WELCOME called for new servers? Don't know, don't care, be safe. */ | |||
if (!IsUser(acptr)) | |||
return HOOK_CONTINUE; | |||
if (after_numeric == RPL_GLOBALUSERS) | |||
{ | |||
/*I haven't read the Unreal source code close enough to know if this is really necessary, but it doesn't hurt anything. */ | |||
if (MyUser(acptr)) | |||
totalrestartcount = totalrestartcount + 1; | |||
m_hyb_statsconn_show_statsconn(acptr); | |||
} | |||
return HOOK_CONTINUE; | |||
} | |||
void m_hyb_statsconn_show_statsconn(Client *acptr) | |||
{ | |||
int l_max_connection_count = max_connection_count; | |||
char flatmap = (FLAT_MAP && !ValidatePermissionsForPath("server:info:lusers",acptr,NULL,NULL,NULL)) ? 1 : 0; | |||
/* If we have a low number of users, the numbers won't make sense. */ | |||
if (l_max_connection_count < (irccounts.me_max + irccounts.me_servers)) | |||
l_max_connection_count = irccounts.me_max + irccounts.me_servers; | |||
if (flatmap) | |||
l_max_connection_count = irccounts.me_max; | |||
sendnumericfmt(acptr, RPL_STATSCONN, RPL_STATSCONN_MESSAGE, | |||
l_max_connection_count, irccounts.me_max, totalrestartcount); | |||
} | |||
</syntaxhighlight>'''/src/modules/third/no-services-plus-q.c''' | |||
Adds +q to the user who creates the channel. Not super useful but InspIRCd used to have a feature like this - especially useful if you do not have services like PissNet. Very simple, possibly a better way to accomplish this but it works for me. | |||
<syntaxhighlight lang="c"> | |||
/* License: GPLv2 or whatever. | |||
* Author: clrx | |||
*/ | |||
#include "unrealircd.h" | |||
ModuleHeader MOD_HEADER | |||
= { | |||
"third/no-services-plus-q", /* name */ | |||
"1.0.0", /* version */ | |||
"NoServicesPlusQ: First user to join channel gets +q(~), useful if your network does not have services.", /* description */ | |||
"clrx", /* author */ | |||
"unrealircd-5", /* do not change this, it indicates module API version */ | |||
}; | |||
int nosvs_local_join(Client *acptr, Channel *channel, MessageTag *mtags, char *parv[]); | |||
MOD_TEST() | |||
{ | |||
return MOD_SUCCESS; | |||
} | |||
MOD_INIT() | |||
{ | |||
#ifdef PREFIX_AQ | |||
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, nosvs_local_join); | |||
#endif | |||
return MOD_SUCCESS; | |||
} | |||
MOD_LOAD() | |||
{ | |||
return MOD_SUCCESS; | |||
} | |||
MOD_UNLOAD() | |||
{ | |||
return MOD_SUCCESS; | |||
} | |||
int nosvs_local_join(Client *acptr, Channel *channel, MessageTag *mtags, char *parv[]) | |||
{ | |||
if (channel->users == 1 && !has_channel_mode(channel, 'P')) | |||
{ | |||
char *lparv[] = { | |||
"+q", | |||
acptr->name, | |||
NULL | |||
}; | |||
do_mode(channel, &me, mtags, 2, lparv, 0, 0); | |||
} | |||
return HOOK_CONTINUE; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 02:35, 1 June 2023
dunes.p.mojavenet.xyz | |
Name | dunes.p.mojavenet.xyz |
---|---|
Location | San Jose, California, USA |
Hosting Provider | Oracle |
Owner | clrx |
SID | 8SL |
Type | Leaf |
Status | Delinked |
ports: 6667, 6697 (SSL) IPv4 & IPv6!
Cool features:
Create a new #channel, get a free +q!
Modules
Name | Function |
---|---|
certificate-refresh | Automatically reload SSL/TLS certificate when the underlying certificate on the filesystem has changed. |
hyb-statsconn | RPL_STATSCONN to new clients and LUSERS, like Hybrid/Ratbox |
no-services-plus-q | Channel creator is automatically given +q/~ |
/src/modules/third/certificate-refresh.c
automatically reload your SSL/TLS certificates when they are changed on the filesystem, no need for a cron job.
/* License: GPLv2
* Author: clrx
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"third/certificate-refresh", /* name */
"1.0.0", /* version */
"Certificate Refresh: Automatically checks SSL/TLS certificates for changes, and reloads SSL/TLS if the certificate has been updated.", /* description */
"clrx", /* author */
"unrealircd-5", /* do not change this, it indicates module API version */
};
/*** <<<MODULE MANAGER START>>>
module
{
// Documentation, as displayed in './unrealircd module info nameofmodule', and possibly at other places:
documentation "https://www.example.org/";
// This is displayed in './unrealircd module info ..' and also if compilation of the module fails:
troubleshooting "In case of problems, check the FAQ at ... or e-mail me at ...";
min-unrealircd-version "5.*";
max-unrealircd-version "5.*";
post-install-text {
"The module is installed. Now all you need to do is add a loadmodule line:";
"loadmodule \"third/certificate-refresh\";";
"And /REHASH the IRCd.";
"The module does not need any other configuration.";
}
}
*** <<<MODULE MANAGER END>>>
*/
#define _MCRF_USE_REINIT_SSL (UNREAL_VERSION_GENERATION == 5 && UNREAL_VERSION_MAJOR == 2 && UNREAL_VERSION_MINOR < 1)
#define _MCRF_TIMEOUT 43200000 /* 12 hours, in milliseconds. */
typedef struct mCRF_Watch_File mCRF_Watch_File;
struct mCRF_Watch_File
{
char *path;
time_t last_modified;
mCRF_Watch_File *next;
};
/* Forward Declarations. */
EVENT(mCRF_timeout);
EVENT(mCRF_setup_files);
time_t mCRF_get_cert_time(const char *path);
mCRF_Watch_File *mCRF_add_watch_file(const char *path);
mCRF_Watch_File *mCRF_find_watch_file(const char *path);
static mCRF_Watch_File *watched_files;
EVENT(mCRF_timeout)
{
TLSOptions *tls_options;
int needReload = 0;
mCRF_Watch_File *wptr;
for (wptr = watched_files; wptr; wptr = wptr->next)
{
time_t ts_curr = mCRF_get_cert_time(wptr->path);
if (ts_curr != wptr->last_modified)
{
needReload = 1;
wptr->last_modified = ts_curr;
}
}
if (needReload)
{
sendto_snomask(SNO_SNOTICE, "*** [third/certificate-refresh]: Server TLS certificate(s) updated, reloading certificates.");
#if _MCRF_USE_REINIT_SSL
reinit_ssl(NULL);
#else
reinit_tls();
#endif
}
}
EVENT(mCRF_setup_files)
{
ConfigItem_listen *lptr;
ConfigItem_sni *sptr;
TLSOptions *tls_options = get_tls_options_for_client(&me);
if (tls_options)
mCRF_add_watch_file(tls_options->certificate_file);
for (lptr = conf_listen; lptr; lptr = lptr->next)
{
tls_options = lptr->tls_options;
if (tls_options)
mCRF_add_watch_file(tls_options->certificate_file);
}
for (sptr = conf_sni; sptr; sptr = sptr->next)
{
mCRF_add_watch_file(sptr->tls_options->certificate_file);
}
}
time_t mCRF_get_cert_time(const char *path)
{
time_t r = 0;
struct stat st;
if (!stat(path, &st))
{
r = st.st_mtime;
}
return r;
}
mCRF_Watch_File *mCRF_add_watch_file(const char *path)
{
mCRF_Watch_File *wptr;
if (path == NULL)
return NULL;
wptr = mCRF_find_watch_file(path);
if (wptr)
return wptr;
wptr = safe_alloc(sizeof(mCRF_Watch_File));
memset(wptr, 0, sizeof(mCRF_Watch_File));
wptr->path = our_strdup(path);
wptr->last_modified = mCRF_get_cert_time(wptr->path);
if (watched_files == NULL)
{
watched_files = wptr;
}
else
{
mCRF_Watch_File *tmp, *prv;
tmp = watched_files;
while (tmp != NULL)
{
prv = tmp;
tmp = tmp->next;
}
prv->next = wptr;
}
return wptr;
}
mCRF_Watch_File *mCRF_find_watch_file(const char *path)
{
mCRF_Watch_File *wptr;
for (wptr = watched_files; wptr; wptr = wptr->next)
{
if (smycmp(path, wptr->path) == 0)
return wptr;
}
return NULL;
}
MOD_INIT()
{
EventAdd(modinfo->handle, "mCRF_timeout", mCRF_timeout, NULL, _MCRF_TIMEOUT, 0);
EventAdd(modinfo->handle, "mCRF_setup_files", mCRF_setup_files, NULL, 5 * 1000, 1);
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
mCRF_Watch_File *wptr, *wptri;
for (wptr = watched_files; wptr; wptr = wptri)
{
wptri = wptr->next;
safe_free(wptr->path);
safe_free(wptr);
}
return MOD_SUCCESS;
}
MOD_TEST()
{
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
/src/modules/third/hyb-statsconn.c
Shows RPL_STATSCONN upon client connection and when LUSERS is called. Doesn't add a lot of value otherwise, but I like how it shows how many actual IRC clients (as opposed to all accept()s in /STATS T) have connected to the server during the current run. I named it hybrid-statsconn since that is the first time I remember seeing this information, but I don't know where this numeric really came from. Oh well :)
[22:56:45] * There are 6 users and 410 invisible on 147 servers
[22:56:45] * 129 operator(s) online
[22:56:45] * 218 channels formed
[22:56:45] * I have 4 clients and 1 servers
[22:56:45] * Current local users 4, max 6
[22:56:45] * Current global users 416, max 1000001
[22:56:45] * Highest connection count: 7 (6 clients) (26 connections received)
(These were the stats after running for about two weeks without a reboot in the round robin)
/* License: GPLv2
* Author: clrx
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"third/hyb-statsconn", /* name */
"1.0.0", /* version */
"Hyb-StatsConn: Shows RPL_STATSCONN", /* description */
"clrx", /* author */
"unrealircd-5", /* do not change this, it indicates module API version */
};
#define RPL_STATSCONN_MESSAGE ":Highest connection count: %d (%d clients) (%ld connections received)"
int m_hyb_statsconn_hook_welcome(Client *acptr, int after_numeric);
int m_hyb_statsconn_hook_local_server_connect(Client *acptr);
int m_hyb_statsconn_rehash_complete(void);
CMD_OVERRIDE_FUNC(m_hyb_statsconn_override);
void m_hyb_statsconn_show_statsconn(Client *acptr);
static long totalrestartcount = 0;
/* Keep track of our handle in order to reinstall the override after each rehash. */
Module *myHandle;
MOD_TEST()
{
return MOD_SUCCESS;
}
MOD_INIT()
{
/* Must mark as permanent, or else we will lose our count. */
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1);
myHandle = modinfo->handle;
HookAdd(modinfo->handle, HOOKTYPE_SERVER_CONNECT, 0, m_hyb_statsconn_hook_local_server_connect);
HookAdd(modinfo->handle, HOOKTYPE_WELCOME, 0, m_hyb_statsconn_hook_welcome);
/* Even if the module is marked as permanent, /REHASH will unload the CommandOverride. So, if we rehash, we need to reload the Override. */
HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 999, m_hyb_statsconn_rehash_complete);
return MOD_SUCCESS;
}
MOD_LOAD()
{
/* Install override on first startup, only run once due to permanent module. */
m_hyb_statsconn_rehash_complete();
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
CMD_OVERRIDE_FUNC(m_hyb_statsconn_override)
{
CallCommandOverride(ovr, client, recv_mtags, parc, parv);
/* This is recommended per the Unreal wiki. It's probably unnecessary as at this time
* lusers does not offer a way to kill the client, and sendbufto_one also checks for a
* dead socket. But it doesn't hurt anything and provides future proofing incase
* other innovations are invented!
*/
if (IsDead(client))
return;
if (parc > 1)
{
Client *target = find_server(parv[1], NULL);
if (target != &me)
return;
}
m_hyb_statsconn_show_statsconn(client);
}
int m_hyb_statsconn_rehash_complete(void)
{
/* re-install override after each rehash */
CommandOverrideAdd(myHandle, "LUSERS", m_hyb_statsconn_override);
return HOOK_CONTINUE;
}
int m_hyb_statsconn_hook_local_server_connect(Client *acptr)
{
/* Only care about local clients, but hooked in for all servers per API. Only increment
* if client is a local server. */
if (MyConnect(acptr) && IsServer(acptr))
totalrestartcount = totalrestartcount + 1;
return HOOK_CONTINUE;
}
int m_hyb_statsconn_hook_welcome(Client *acptr, int after_numeric)
{
/* Is HOOK_WELCOME called for new servers? Don't know, don't care, be safe. */
if (!IsUser(acptr))
return HOOK_CONTINUE;
if (after_numeric == RPL_GLOBALUSERS)
{
/*I haven't read the Unreal source code close enough to know if this is really necessary, but it doesn't hurt anything. */
if (MyUser(acptr))
totalrestartcount = totalrestartcount + 1;
m_hyb_statsconn_show_statsconn(acptr);
}
return HOOK_CONTINUE;
}
void m_hyb_statsconn_show_statsconn(Client *acptr)
{
int l_max_connection_count = max_connection_count;
char flatmap = (FLAT_MAP && !ValidatePermissionsForPath("server:info:lusers",acptr,NULL,NULL,NULL)) ? 1 : 0;
/* If we have a low number of users, the numbers won't make sense. */
if (l_max_connection_count < (irccounts.me_max + irccounts.me_servers))
l_max_connection_count = irccounts.me_max + irccounts.me_servers;
if (flatmap)
l_max_connection_count = irccounts.me_max;
sendnumericfmt(acptr, RPL_STATSCONN, RPL_STATSCONN_MESSAGE,
l_max_connection_count, irccounts.me_max, totalrestartcount);
}
/src/modules/third/no-services-plus-q.c
Adds +q to the user who creates the channel. Not super useful but InspIRCd used to have a feature like this - especially useful if you do not have services like PissNet. Very simple, possibly a better way to accomplish this but it works for me.
/* License: GPLv2 or whatever.
* Author: clrx
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"third/no-services-plus-q", /* name */
"1.0.0", /* version */
"NoServicesPlusQ: First user to join channel gets +q(~), useful if your network does not have services.", /* description */
"clrx", /* author */
"unrealircd-5", /* do not change this, it indicates module API version */
};
int nosvs_local_join(Client *acptr, Channel *channel, MessageTag *mtags, char *parv[]);
MOD_TEST()
{
return MOD_SUCCESS;
}
MOD_INIT()
{
#ifdef PREFIX_AQ
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, nosvs_local_join);
#endif
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
int nosvs_local_join(Client *acptr, Channel *channel, MessageTag *mtags, char *parv[])
{
if (channel->users == 1 && !has_channel_mode(channel, 'P'))
{
char *lparv[] = {
"+q",
acptr->name,
NULL
};
do_mode(channel, &me, mtags, 2, lparv, 0, 0);
}
return HOOK_CONTINUE;
}