/**
* -----------------------------------------------------------------------------
* Name : ns_ajoin
* Author : Dj-RuFfy ( Founder & CEO EasyNets Corp )
* Date : 23/10/2006
* Version : 4.2.1
* -----------------------------------------------------------------------------
* Limitations: Any IRCd which supports SVSJOIN
* Tested : Anope-1.7.21 + UnrealIRCd 3.2.6
* Requires : Anope-1.7.20
* -----------------------------------------------------------------------------
* This module adds the AJOIN command to nickserv allowing users to maintain
* a server side auto-join list.
* The ajoin settings can be configured for each nick group through SET AJOIN.
*
* This module has been designed and tested for Anope-1.7.21 and will not
* properly work on any versions prior to 1.7.20! Changes have been made to
* SVSJOIN support for both inspircd and UnrealIRCd. Without these users
* may be able to pass through channels bans (insp) or not get into +k chans.
*
* This module requires the IRCd to support SVSJOIN.
* At the time of writing this module will only properly work with following IRCDs:
* - InspIRCd
* - Plexus 3.0 [or later]
* - PTLink
* - UltimateIRCd 2.8.2 [or later] [Excluding 3.x]
* - Unreal 3.1.1 [or later]
* - Unreal 3.2 [or later]
* - ViagraIRCd 1.3.x [or later]
*
* I wrote this module because the other 2 alternatives (addon module by
* DukePyrolator and ns_ajoin by Scott) are outdated and require MySQL, which
* i - like many other people probably - do not use / want to use.
*
* The database subroutines have been ported from swhois by Trystan,
* I wouldn't have been able to pull this off without those...
*
* Note that this module should be placed in ModuleDelayedAutoload.
* -----------------------------------------------------------------------------
* Translations:
*
* - English and Dutch Languages provided and maintained by myself.
* - German translation : SNU.
* - Turkish translation : ice
* - Russian translation : Kein
* -----------------------------------------------------------------------------
*
* Changelog:
*
* 4.2.1 Fixed AJOIN not requiring password identify (Reported by ysfm).
* Fixed ajoining suspended channels on identify.
* Fixed adding forbidden/suspended channels to the ajoin list.
* Fixed ChanServ replying to AJOIN ADD if the channel is not registered.
*
* 4.2.0 Going to next minor version and considering stable.
* Added ability to delete and list entries by number/list.
* Added russian translation (Provided by Kein)
* Fixed a few typos.
* Fixed crashbug when autojoining to dropped/expired channels. (Thanks to "Aded")
* Increased default number of entries allowed in AutoJoin list to 12
*
* 4.1.15 Fixed module causing a crash when ajoin issued by an unregistered user. (Thanks to "aerolife")
*
* 4.1.14 Fixed ajoin failing under several conditions on a channel with +k..
* Added ban testing and unbanning if possible...
* Because UnrealIRCd is the only IRCd supporting SVSJOIN'ing to +k chans
* we now invite before joining on all IRCd's except unreal.
* We no longer unban the user if he is excepted...
* Added hooks for NS ID.
*
* 4.1.13 Limited channels on ajoin list to registered channels only.
* This should resolve issues with invalid channel names. (Reported by "Avenger")
* Added check before executing the SVSJOIN to make sure channel isn't forbidden.
* Fixed bug causing remaining ajoins to be aborted if user is already on a ajoin chan.
*
* 4.1.12 Removed remaining bit of debug code
* Fixed some minor language details (Complained about by "SNU" ;-) )
* Added german translation (Provided by SNU)
*
* 4.1.11 Fixed possible segfault when loading is aborted because of unsupported ircd
* Fixed missing help for SET AJOIN.
*
* 4.1.10 Fixed segfault when saving DB after nick was dropped/expired. (Reported by "hexa")
* (Rewrote part of DB saving routine and the way AjoinEntries are addressed.(ugly!))
* Added InspIRCd as supported IRCd. (Their DOCs say it implements SVSJOIN so..)
* Added turkish translation (Provided by ice)
*
* 4.1.9 Added win32 support
* Fixed segfault when AJOIN is used by an unregistered user
*
* 4.1.8 Fixed vhost being set after ajoining (Reported by "CuttingEdge")
* Now also performing ajoin on UPDATE
*
* 4.1.7 Fixed crashes in ajoin add/del
* Added Anope version check
* Default ajoin flags moved to a const
* No longer storing empty entries with default configuration to the DB
*
* 4.1.6 Added SET AJOIN option
* Changed database scheme to include version info.
* Fixed a memleak in the AjoinEntry handlers.
*
* 4.1.5 Fixed segfaults on load & unload
* AnopeFini now goes over the ajoinTable.. should speed unloading up.
* Anope now also backs the ajoin database up.
*
* 4.1.4 Finished runtime storing and database writing and reading.
* Implemented ajoining on identify.
*
* 4.1.3 Replaced hardcoded replies with langtables.
*
* 4.1.2 Replaced database system
*
* 4.1.1 First 'working' Alpha Version
*
* 4.1 Module Development taken over by me (Dj-RuFfy)
* Beginning with clean development version.
*
* -----------------------------------------------------------------------------
**/
/**
* TODO:
*
* - default entries in ajoin: a /msg nickserv ajoin default (add,del,list,clear)
* which is only available to opers
*
**/
/**
* Configuration directives that should be copy-pasted to services.conf
# AJoinDB [OPTIONAL]
# Module: ns_ajoin
#
# Use the given filename as database to store the AJOINs.
# If not given, the default of "ns_ajoin.db" will be used.
#
#AJoinDB "ns_ajoin.db"
*
**/
/*------------------------------Configuration Block----------------------------*/
/**
* Determines the maximum entries in an ajoin list.
*
* If this value is changed, it will not affect AjoinEntries allready in the
* database, however now ones can't be added if the limit is exceeded.
**/
int NSAjoinMax = 12;
/*-------------------------End of Configuration Block--------------------------*/
#include "module.h"
#define AUTHOR "Dj-RuFfy"
#define VERSION "4.2.1"
#define AJOINDBVERSION 1
/* Language defines */
#define LANG_NUM_STRINGS 29
#define LANG_AJOIN_DESC 0
#define LANG_AJOIN_SYNTAX 1
#define LANG_AJOIN_SYNTAX_EXT 2
#define LANG_AJOIN_DISABLED 3
#define LANG_NO_LOCAL_CHAN 4
#define LANG_CHAN_SYMB_REQUIRED 5
#define LANG_CHAN_UPDATED 6
#define LANG_AJOIN_LIST_FULL 7
#define LANG_CHAN_ADDED 8
#define LANG_NO_AJOINS 9
#define LANG_AJOIN_LIST_EMPTY 10
#define LANG_NO_ENTRY 11
#define LANG_CHAN_DELETED 12
#define LANG_AJOIN_ENTRY 13
#define LANG_AJOIN_ENTRIES 14
#define LANG_AJOIN_LIST_CLEARED 15
#define LANG_UNKWN_AJOIN_OPTION 16
#define LANG_AJOINING 17
#define LANG_AJOINING_FAILED 18
#define LANG_SET_AJOIN_DESC 19
#define LANG_SET_AJOIN_SYNTAX 20
#define LANG_SET_AJOIN_SYNTAX_EXT 21
#define LANG_SET_AJOIN_ON 22
#define LANG_SET_AJOIN_SILENT 23
#define LANG_SET_AJOIN_OFF 24
#define LANG_AJOINING_SUM_SUCCESS 25
#define LANG_AJOINING_SUM_FAILED 26
#define LANG_AJOIN_DELETED_NR_1 27
#define LANG_AJOIN_DELETED_NR 28
/* Flags to set in database */
#define AJOIN_ON (1 << 0)
#define AJOIN_SILENT (1 << 1)
/* Database seperators */
#define SEPARATOR ':' /* End of a key, seperates keys from values */
#define BLOCKEND '\n' /* End of a block, e.g. a whole nick/channel or a subblock */
#define VALUEEND '\000' /* End of a value */
#define SUBSTART '\010' /* Beginning of a new subblock, closed by a BLOCKEND */
/* Database reading return values */
#define DB_READ_SUCCESS 0
#define DB_READ_ERROR 1
#define DB_EOF_ERROR 2
#define DB_VERSION_ERROR 3
#define DB_READ_BLOCKEND 4
#define DB_READ_SUBSTART 5
#define DB_WRITE_SUCCESS 0
#define DB_WRITE_ERROR 1
#define DB_WRITE_NOVAL 2
/* Database Key, Value max length */
#define MAXKEYLEN 128
#define MAXVALLEN 1024
/* Structs */
typedef struct db_file_ DBFile;
typedef struct ajoinchan_ AjoinChan;
typedef struct ajoinentry_ AjoinEntry;
struct db_file_ {
FILE *fptr; /* Pointer to the opened file */
int db_version; /* The db version of the datafiles (only needed for reading) */
int core_db_version; /* The current db version of this anope source */
char service[256]; /* StatServ/etc. */
char filename[256]; /* Filename of the database */
char temp_name[262]; /* Temp filename of the database */
};
struct ajoinchan_ {
AjoinChan *prev, *next;
char *channel;
char *key;
int nr;
};
struct ajoinentry_ {
/* Specifies storage location in ajoinTable */
int row, col;
AjoinEntry *prev, *next;
int ajchannels;
AjoinChan *channels;
/* Flags store AJOIN settings.
* Bits are used as followed:
* User wants to be autojoined {bit 0}
* User wants autojoin to be silent {bit 1}
*/
uint16 flags;
/* Used to check during the cleanup whether the entry is still being used.
* This is always 0 except when the DB saving function has completed going
* over all NickCore's. The ajoin entries with in_use set to 0 at that time
* are no longer used and cleared. In the other entries in_use is reset to 0. */
int in_use;
};
/* Constants */
char *DefAJoinDB = "ns_ajoin.db";
char *ModDataKey = "ajoin";
uint16 DefAjoinFlags = 1;
/* Variables */
char *AJoinDB;
/* This stores the last used row in teh ajoinTable so the module can attempt to
* spread the entries out as evenly as possible... */
int counter;
/* We still need a table cause we won't be able to reach the AjoinEntry if
* the nick is dropped... this sucks i know, but it s just a list of references */
/* Table will be cleaned of invalid entries every time the database is saved */
AjoinEntry *ajoinTable[1024];
/* Functions */
void do_help_list(User *u);
int do_help(User *u);
int add_ajoin_option(User *u);
int do_set_ajoin_help(User *u);
int do_ajoin(User *u);
int do_identify(User *u);
int ns_set(User *u);
int null_func(User *u);
int is_banned(ChannelInfo *ci, User *u);
int new_open_db_read(DBFile *dbptr, char **key, char **value);
int new_open_db_write(DBFile *dbptr);
void new_close_db(FILE *fptr, char **key, char **value);
int new_read_db_entry(char **key, char **value, FILE * fptr);
int new_write_db_entry(const char *key, DBFile *dbptr, const char *fmt, ...);
int new_write_db_endofblock(DBFile *dbptr);
void fill_db_ptr(DBFile *dbptr, int version, int core_version, char service[256], char filename[256]);
static int ajoin_del_callback(User * u, int num, va_list args);
static int ajoin_list_callback(User * u, int num, va_list args);
static AjoinChan *addAjoinChan(AjoinEntry *ae, char *chan, char *key);
AjoinChan *findAjoinChan(AjoinEntry *ae, char *chan);
AjoinChan *findAjoinChanNr(AjoinEntry *ae, int nr);
static int deleteAjoinChan(AjoinEntry *ae, AjoinChan *ac);
static int clearAjoinChans(AjoinEntry *ae);
static int numberAjoinChans(AjoinEntry *ae);
static AjoinEntry *createAjoinEntry(NickCore *nc);
AjoinEntry *getAjoinEntry(NickCore *nc);
static int deleteAjoinEntry(AjoinEntry *ae);
void freeUnusedEntries();
int do_save(int argc, char **argv);
int db_backup(int argc, char **argv);
void load_ajoin_db(void);
void save_ajoin_db(void);
AjoinEntry *go_to_next_entry(uint16 skipped, AjoinEntry *next, AjoinEntry *ae);
int valid_ircd(void);
void load_config(void);
int reload_config(int argc, char **argv);
void add_languages(void);
/* ------------------------------------------------------------------------------- */
/**
* Create the command, and tell anope about it.
* @param argc Argument count
* @param argv Argument list
* @return MOD_CONT to allow the module, MOD_STOP to stop it
**/
int AnopeInit(int argc, char **argv) {
Command *c;
EvtHook *hook;
alog("[\002ns_ajoin\002] Loading module...");
counter = 0;
if (!valid_ircd()) {
alog("[\002ns_ajoin\002] ERROR: IRCd not supported by this module");
alog("[\002ns_ajoin\002] Unloading module...");
return MOD_STOP;
}
if (!moduleMinVersion(1,7,21,1341)) {
alog("[\002ns_ajoin\002] Your version of Anope isn't supported. Please update to a newer release.");
return MOD_STOP;
}
/* Create AJOIN command.. */
c = createCommand("AJOIN", do_ajoin, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV,c,MOD_HEAD) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot create AJOIN command...");
return MOD_STOP;
}
moduleAddHelp(c,do_help);
moduleSetNickHelp(do_help_list);
/* Hook to the IDENTIFY and UPDATE command */
c = createCommand("IDENTIFY", do_identify, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV,c,MOD_TAIL) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot hook to IDENTIFY command...");
return MOD_STOP;
}
c = createCommand("ID", do_identify, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV,c,MOD_TAIL) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot hook to ID command...");
return MOD_STOP;
}
c = createCommand("UPDATE", do_identify, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV,c,MOD_TAIL) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot hook to UPDATE command...");
return MOD_STOP;
}
/* Create the SET AJOIN command, its help and add it to cmd listing.. */
c = createCommand("SET", ns_set, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV, c, MOD_HEAD) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot create SET AJOIN command...");
return MOD_STOP;
}
c = createCommand("SET AJOIN", NULL, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV, c, MOD_TAIL) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot create help for the SET AJOIN command...");
return MOD_STOP;
}
moduleAddHelp(c, do_set_ajoin_help);
c = createCommand("SET", null_func, NULL, -1, -1, -1, -1, -1);
if (moduleAddCommand(NICKSERV, c, MOD_TAIL) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Cannot add AJOIN to SET options...");
return MOD_STOP;
}
moduleAddHelp(c, add_ajoin_option);
/* Hook to some events.. */
hook = createEventHook(EVENT_RELOAD, reload_config);
if (moduleAddEventHook(hook) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Can't hook to EVENT_RELOAD event");
return MOD_STOP;
}
hook = createEventHook(EVENT_DB_SAVING, do_save);
if (moduleAddEventHook(hook) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Can't hook to EVENT_DB_SAVING event");
return MOD_STOP;
}
hook = createEventHook(EVENT_DB_BACKUP, db_backup);
if (moduleAddEventHook(hook) != MOD_ERR_OK) {
alog("[\002ns_ajoin\002] Can't hook to EVENT_DB_BACKUP event");
return MOD_STOP;
}
load_config();
add_languages();
load_ajoin_db();
moduleAddAuthor(AUTHOR);
moduleAddVersion(VERSION);
alog("[\002ns_ajoin\002] Module loaded successfully...");
return MOD_CONT;
}
/**
* Unload the module
**/
void AnopeFini(void) {
AjoinEntry *ae = NULL, *next = NULL;
int i;
if (AJoinDB)
save_ajoin_db();
/* clear the memory.... */
for (i = 0; i < 1024; i++) {
for (ae = ajoinTable[i]; ae; ae = next) {
next = ae->next;
deleteAjoinEntry(ae);
}
}
if (AJoinDB)
free(AJoinDB);
alog("[\002ns_ajoin\002] Unloading module...");
}
/* ------------------------------------------------------------------------------- */
/**
* Add the AJOIN command to the NickServ HELP listing.
**/
void do_help_list(User *u) {
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DESC);
}
/**
* Show the extended help on the AJOIN command.
**/
int do_help(User *u) {
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_SYNTAX_EXT);
return MOD_CONT;
}
/**
* Add the AJOIN option to the end of the SET options listing.
**/
int add_ajoin_option(User *u) {
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_DESC);
return MOD_CONT;
}
/**
* Show the extended help on the SET AJOIN command.
**/
int do_set_ajoin_help(User *u) {
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_SYNTAX_EXT);
return MOD_CONT;
}
/* ------------------------------------------------------------------------------- */
int do_ajoin(User *u) {
ChannelInfo *ci;
char *buffer, *cmd, *chan, *key;
buffer = moduleGetLastBuffer();
cmd = myStrGetToken(buffer, ' ', 0);
chan = myStrGetToken(buffer, ' ', 1);
key = myStrGetToken(buffer, ' ', 2);
if (!cmd)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_SYNTAX);
else if (stricmp(cmd, "ADD") == 0) {
if (readonly)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DISABLED);
else if (!u->na)
notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
else if (!nick_identified(u))
notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
else if (!chan)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_SYNTAX);
else if (*chan == '&')
moduleNoticeLang(s_NickServ, u, LANG_NO_LOCAL_CHAN);
else if (*chan != '#')
moduleNoticeLang(s_NickServ, u, LANG_CHAN_SYMB_REQUIRED);
else if (!anope_valid_chan(chan))
notice_lang(s_NickServ, u, CHAN_X_INVALID, chan);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_NickServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_NickServ, u, CHAN_X_FORBIDDEN, chan);
else if (ci->flags & CI_SUSPENDED)
notice_lang(s_NickServ, u, CHAN_X_FORBIDDEN, chan);
else {
AjoinEntry *ae = NULL;
AjoinChan *ac = NULL;
ae = getAjoinEntry(u->na->nc);
ac = findAjoinChan(ae, chan);
if (!ae)
ae = createAjoinEntry(u->na->nc);
if (ac) {
/* update existing entry */
if (!key && ac->key) {
free(ac->key);
ac->key = NULL;
} else if (key) {
free(ac->key);
ac->key = sstrdup(key);
}
moduleNoticeLang(s_NickServ, u, LANG_CHAN_UPDATED);
} else {
if (ae->ajchannels == NSAjoinMax)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_LIST_FULL);
else {
addAjoinChan(ae, chan, key);
moduleNoticeLang(s_NickServ, u, LANG_CHAN_ADDED, chan);
}
}
}
} else if (stricmp(cmd, "DEL") == 0) {
AjoinEntry *entry;
if (readonly)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DISABLED);
else if (!u->na)
notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
else if (!chan)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_SYNTAX);
else if (!(entry = getAjoinEntry(u->na->nc)))
moduleNoticeLang(s_NickServ, u, LANG_NO_AJOINS);
else {
/* we r a valid chan, delete us if we r present... */
if (entry->ajchannels == 0)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_LIST_EMPTY);
else {
/* Check if the given target is a number/list.
* Only search if it isn't. */
if (isdigit(*chan) && strspn(chan, "1234567890,-") == strlen(chan)) {
int deleted, count;
deleted = process_numlist(chan, &count, ajoin_del_callback, u, entry);
numberAjoinChans(entry);
if (deleted == 1)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DELETED_NR_1, deleted);
else
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DELETED_NR, deleted);
} else {
if (*chan != '#')
moduleNoticeLang(s_NickServ, u, LANG_CHAN_SYMB_REQUIRED);
else {
AjoinChan *ac = findAjoinChan(entry, chan);
if (!ac)
moduleNoticeLang(s_NickServ, u, LANG_NO_ENTRY, chan);
else {
deleteAjoinChan(entry, ac);
numberAjoinChans(entry);
moduleNoticeLang(s_NickServ, u, LANG_CHAN_DELETED, chan);
}
}
}
}
}
} else if (stricmp(cmd, "LIST") == 0) {
int i = 0;
AjoinEntry *ae = NULL;
AjoinChan *ac = NULL;
if (!u->na) {
notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
} else {
ae = getAjoinEntry(u->na->nc);
if (!ae)
moduleNoticeLang(s_NickServ, u, LANG_NO_AJOINS);
else {
if (ae->ajchannels == 0)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_LIST_EMPTY);
else {
/* Check if the given target is a number/list. */
if (chan && isdigit(*chan) && strspn(chan, "1234567890,-") == strlen(chan)) {
int count, found;
found = process_numlist(chan, &count, ajoin_list_callback, u, ae);
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_ENTRIES, found);
} else {
for (ac = ae->channels; ac; ac = ac->next) {
if (chan && !match_wild_nocase(chan, ac->channel))
continue;
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_ENTRY, ac->nr, ac->channel,
((ac->key) ? ac->key:""));
i++;
}
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_ENTRIES, i);
}
}
}
}
} else if (stricmp(cmd, "CLEAR") == 0) {
AjoinEntry *ae;
if (readonly)
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_DISABLED);
else if (!u->na)
notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
else if (!(ae = getAjoinEntry(u->na->nc)))
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_LIST_EMPTY);
else {
clearAjoinChans(ae);
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_LIST_CLEARED);
}
} else {
moduleNoticeLang(s_NickServ, u, LANG_UNKWN_AJOIN_OPTION);
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_SYNTAX);
}
if (cmd)
free(cmd);
if (chan)
free(chan);
if (key)
free(key);
return MOD_CONT;
}
int do_identify(User *u) {
int i = 0, ok, needinvite, counter_succ = 0, counter_f = 0;
if (nick_identified(u) && u->na->nc) {
AjoinEntry *ae = NULL;
ae = getAjoinEntry(u->na->nc);
if (ae && (ae->flags & AJOIN_ON) && (ae->ajchannels > 0)) {
AjoinChan *ac = NULL;
ac = ae->channels;
for (i = 0; i < ae->ajchannels; i++) {
Channel *c = NULL;
ChannelInfo *ci;
ok = 1, needinvite = 0;
if (!ac)
break;
/* If the channel is no longer registered we won't complain..
* We are sure the name is valid because it was registered once.
* We do complain when the channel is forbidden.. no point in joining. */
/* We do the same when it s suspended. We do NOT automatically remove channels though. */
ci = cs_findchan(ac->channel);
if (ci && (ci->flags & CI_VERBOTEN)) {
notice_lang(s_NickServ, u, CHAN_X_FORBIDDEN, ac->channel);
ok = 0;
}
if (ci && (ci->flags & CI_SUSPENDED)) {
notice_lang(s_NickServ, u, CHAN_X_FORBIDDEN, ac->channel);
ok = 0;
}
/* Check if channel already exists...
* We can't use c->ci here because we don't require the chan to still be registered */
if (ok && ((c = findchan(ac->channel)))) {
if (is_on_chan(c, u)) {
ac = ac->next;
continue;
}
else {
if (c->key) {
if (!ac->key || (stricmp(ac->key, c->key) != 0)) {
if (ci && check_access(u, ci, CA_GETKEY)) {
if (ac->key)
free(ac->key);
ac->key = sstrdup(c->key);
} else
needinvite = 1;
}
/* Any IRCd other then unreal needs invite cause they don't support
* SVSJOIN with a key.. */
if (stricmp(IRCDModule,"unreal32"))
needinvite = 1;
}
/* Check if user is banned and if so, remove is possible.
* If we cannot remove it, try to invite */
if (ci && is_banned(ci, u)) {
if (check_access(u, ci, CA_UNBAN))
common_unban(ci, u->nick);
else
needinvite = 1;
}
if ((c->mode & anope_get_invite_mode()) || needinvite) {
if (ci && check_access(u, ci, CA_INVITE))
anope_cmd_invite(s_NickServ, ac->channel, u->nick);
else
ok = 0;
}
}
}
if (ok) {
if (ae->flags & AJOIN_SILENT)
counter_succ++;
else
moduleNoticeLang(s_NickServ, u, LANG_AJOINING, ac->channel);
anope_cmd_svsjoin(s_NickServ, u->nick, ac->channel, ac->key);
} else {
if (ae->flags & AJOIN_SILENT)
counter_f++;
else
moduleNoticeLang(s_NickServ, u, LANG_AJOINING_FAILED, ac->channel);
}
ac = ac->next;
}
if (ae->flags & AJOIN_SILENT) {
if (counter_succ > 0)
moduleNoticeLang(s_NickServ, u, LANG_AJOINING_SUM_SUCCESS, counter_succ);
if (counter_f > 0)
moduleNoticeLang(s_NickServ, u, LANG_AJOINING_SUM_FAILED, counter_f);
}
}
}
return MOD_CONT;
}
int ns_set(User *u) {
char *args, *cmd, *option;
/* If readonly, let the core ns_set handle it.. */
if (readonly)
return MOD_CONT;
args = moduleGetLastBuffer();
if (!args)
return MOD_CONT;
cmd = myStrGetToken(args, ' ', 0);
option = myStrGetToken(args, ' ', 1);
if (cmd && !stricmp(cmd, "AJOIN")) {
if (!option)
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_SYNTAX);
else if (!u->na)
notice_lang(s_NickServ, u, NICK_NOT_REGISTERED);
else if (!nick_identified(u))
notice_lang(s_NickServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
else {
AjoinEntry *ae = NULL;
ae = getAjoinEntry(u->na->nc);
if (!ae)
ae = createAjoinEntry(u->na->nc);
if (!stricmp(option, "ON")) {
/* Turn off silent bit */
ae->flags &= ~AJOIN_SILENT;
/* Turn on use autojoin bit */
ae->flags |= AJOIN_ON;
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_ON);
} else if (!stricmp(option, "SILENT")) {
/* Turn on silent bit */
ae->flags |= AJOIN_SILENT;
/* Turn on use autojoin bit */
ae->flags |= AJOIN_ON;
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_SILENT);
} else if (!stricmp(option, "OFF")) {
/* Turn off silent bit */
ae->flags &= ~AJOIN_SILENT;
/* Turn off use autojoin bit */
ae->flags &= ~AJOIN_ON;
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_OFF);
} else
moduleNoticeLang(s_NickServ, u, LANG_SET_AJOIN_SYNTAX);
}
free(cmd);
if (option) free (option);
return MOD_STOP;
}
if (cmd) free(cmd);
if (option) free (option);
return MOD_CONT;
}
int null_func(User *u) {
return MOD_CONT;
}
int is_banned(ChannelInfo *ci, User *u) {
int i, isbanned = 0;
char *host = NULL;
if (!ci->c)
return 0;
host = host_resolve(u->host);
for (i = 0; i < ci->c->bancount; i++) {
if (match_usermask(ci->c->bans[i], u) ||
(host && match_userip(ci->c->bans[i], u, host))) {
isbanned = 1;
break;
}
}
if (host)
free(host);
return isbanned;
}
/* ------------------------------------------------------------------------------- */
/**************************************************************************
* Generic DataBase Functions (Taken from swhois by Trystan)
**************************************************************************/
int new_open_db_read(DBFile *dbptr, char **key, char **value) {
*key = malloc(MAXKEYLEN);
*value = malloc(MAXVALLEN);
if (!(dbptr->fptr = fopen(dbptr->filename, "rb"))) {
if (debug) {
alog("debug: Can't read %s database %s : errno(%d)", dbptr->service,
dbptr->filename, errno);
}
free(*key);
*key = NULL;
free(*value);
*value = NULL;
return DB_READ_ERROR;
}
dbptr->db_version = fgetc(dbptr->fptr) << 24 | fgetc(dbptr->fptr) << 16
| fgetc(dbptr->fptr) << 8 | fgetc(dbptr->fptr);
if (ferror(dbptr->fptr)) {
if (debug) {
alog("debug: Error reading version number on %s", dbptr->filename);
}
free(*key);
*key = NULL;
free(*value);
*value = NULL;
return DB_READ_ERROR;
} else if (feof(dbptr->fptr)) {
if (debug) {
alog("debug: Error reading version number on %s: End of file detected",
dbptr->filename);
}
free(*key);
*key = NULL;
free(*value);
*value = NULL;
return DB_EOF_ERROR;
} else if (dbptr->db_version < 1) {
if (debug) {
alog("debug: Invalid version number (%d) on %s", dbptr->db_version, dbptr->filename);
}
free(*key);
*key = NULL;
free(*value);
*value = NULL;
return DB_VERSION_ERROR;
}
return DB_READ_SUCCESS;
}
int new_open_db_write(DBFile *dbptr) {
if (!(dbptr->fptr = fopen(dbptr->filename, "wb"))) {
if (debug) {
alog("debug: %s Can't open %s database for writing", dbptr->service, dbptr->filename);
}
return DB_WRITE_ERROR;
}
if (fputc(dbptr->core_db_version >> 24 & 0xFF, dbptr->fptr) < 0 ||
fputc(dbptr->core_db_version >> 16 & 0xFF, dbptr->fptr) < 0 ||
fputc(dbptr->core_db_version >> 8 & 0xFF, dbptr->fptr) < 0 ||
fputc(dbptr->core_db_version & 0xFF, dbptr->fptr) < 0) {
if (debug) {
alog("debug: Error writing version number on %s", dbptr->filename);
}
return DB_WRITE_ERROR;
}
return DB_WRITE_SUCCESS;
}
void new_close_db(FILE *fptr, char **key, char **value) {
if (key && *key) {
free(*key);
*key = NULL;
}
if (value && *value) {
free(*value);
*value = NULL;
}
if (fptr) {
fclose(fptr);
}
}
int new_read_db_entry(char **key, char **value, FILE *fptr) {
char *string = *key;
int character;
int i = 0;
**key = '\0';
**value = '\0';
while (1) {
if ((character = fgetc(fptr)) == EOF) { /* a problem occurred reading the file */
if (ferror(fptr)) {
return DB_READ_ERROR; /* error! */
}
return DB_EOF_ERROR; /* end of file */
} else if (character == BLOCKEND) { /* END OF BLOCK */
return DB_READ_BLOCKEND;
} else if (character == VALUEEND) { /* END OF VALUE */
string[i] = '\0'; /* end of value */
return DB_READ_SUCCESS;
} else if (character == SEPARATOR) { /* END OF KEY */
string[i] = '\0'; /* end of key */
string = *value; /* beginning of value */
i = 0; /* start with the first character of our value */
} else {
if ((i == (MAXKEYLEN - 1)) && (string == *key)) { /* max key length reached, continuing with value */
string[i] = '\0'; /* end of key */
string = *value; /* beginning of value */
i = 0; /* start with the first character of our value */
} else if ((i == (MAXVALLEN - 1)) && (string == *value)) { /* max value length reached, returning */
string[i] = '\0';
return DB_READ_SUCCESS;
} else {
string[i] = character; /* read string (key or value) */
i++;
}
}
}
}
int new_write_db_entry(const char *key, DBFile *dbptr, const char *fmt, ...) {
char string[MAXKEYLEN + MAXVALLEN + 2], value[MAXVALLEN]; /* safety byte :P */
va_list ap;
unsigned int length;
if (!dbptr) {
return DB_WRITE_ERROR;
}
va_start(ap, fmt);
vsnprintf(value, MAXVALLEN, fmt, ap);
va_end(ap);
if (!stricmp(value, "(null)")) {
return DB_WRITE_NOVAL;
}
snprintf(string, MAXKEYLEN + MAXVALLEN + 1, "%s%c%s", key, SEPARATOR, value);
length = strlen(string);
string[length] = VALUEEND;
length++;
if (fwrite(string, 1, length, dbptr->fptr) < length) {
if (debug) {
alog("debug: Error writing to %s", dbptr->filename);
}
new_close_db(dbptr->fptr, NULL, NULL);
if (debug) {
alog("debug: Restoring backup.");
}
remove(dbptr->filename);
rename(dbptr->temp_name, dbptr->filename);
free(dbptr);
dbptr = NULL;
return DB_WRITE_ERROR;
}
return DB_WRITE_SUCCESS;
}
int new_write_db_endofblock(DBFile *dbptr) {
if (!dbptr) {
return DB_WRITE_ERROR;
}
if (fputc(BLOCKEND, dbptr->fptr) == EOF) {
if (debug) {
alog("debug: Error writing to %s", dbptr->filename);
}
new_close_db(dbptr->fptr, NULL, NULL);
return DB_WRITE_ERROR;
}
return DB_WRITE_SUCCESS;
}
void fill_db_ptr(DBFile *dbptr, int version, int core_version,
char service[256], char filename[256]) {
dbptr->db_version = version;
dbptr->core_db_version = core_version;
if (!service)
strcpy(dbptr->service, service);
else
strcpy(dbptr->service, "");
strcpy(dbptr->filename, filename);
snprintf(dbptr->temp_name, 261, "%s.temp", filename);
return;
}
/* ------------------------------------------------------------------------------- */
static int ajoin_del_callback(User * u, int num, va_list args) {
AjoinEntry *ae = va_arg(args, AjoinEntry *);
AjoinChan *ac = findAjoinChanNr(ae, num);
if (!ac)
return 0;
moduleNoticeLang(s_NickServ, u, LANG_CHAN_DELETED, ac->channel);
deleteAjoinChan(ae, ac);
return 1;
}
static int ajoin_list_callback(User * u, int num, va_list args) {
AjoinEntry *ae = va_arg(args, AjoinEntry *);
AjoinChan *ac = findAjoinChanNr(ae, num);
if (!ac)
return 0;
moduleNoticeLang(s_NickServ, u, LANG_AJOIN_ENTRY, ac->nr, ac->channel,
((ac->key) ? ac->key:""));
return 1;
}
/* ------------------------------------------------------------------------------- */
/**
* We add an AjoinChan to the list and update the nr of the channels.
**/
static AjoinChan *addAjoinChan(AjoinEntry *ae, char *chan, char *key) {
AjoinChan *ac = NULL, *current = NULL, *previous = NULL;
if (!ae || !chan)
return NULL;
if ((ac = malloc(sizeof(AjoinChan))) == NULL) {
fatal("Out Of Memory!");
}
for (current = ae->channels; current; current = current->next) {
/* search for the position to insert the new channel */
if (stricmp(chan, current->channel) > 0)
previous = current;
else
break;
}
ac->next = current;
ac->prev = previous;
ac->channel = sstrdup(chan);
if (key)
ac->key = sstrdup(key);
else
ac->key = NULL;
if (previous == NULL) {
ae->channels = ac;
ac->nr = 1;
} else {
previous->next = ac;
ac->nr = previous->nr + 1;
}
if (current)
current->prev = ac;
while (current) {
current->nr++;
current = current->next;
}
ae->ajchannels++;
return ac;
}
AjoinChan *findAjoinChan(AjoinEntry *ae, char *chan) {
AjoinChan *current;
if (!ae)
return NULL;
for (current = ae->channels; current; current = current->next) {
if (!stricmp(chan, current->channel))
return current;
}
return NULL;
}
AjoinChan *findAjoinChanNr(AjoinEntry *ae, int nr) {
AjoinChan *current;
if (!nr)
return NULL;
for (current = ae->channels; current; current = current->next) {
if (current->nr == nr)
return current;
}
return NULL;
}
/**
* We delete an AjoinChan from the list, but we do not update the numbering.
* This needs to be done later by calling numberAjoinChans() because numbers shouldn't
* change while deleting ajoinchans by number.
**/
static int deleteAjoinChan(AjoinEntry *ae, AjoinChan *ac) {
if (!ae || !ac)
return 0;
if (ac->prev)
ac->prev->next = ac->next;
else
ae->channels = ac->next;
if (ac->next)
ac->next->prev = ac->prev;
if (ac->channel)
free(ac->channel);
if (ac->key)
free(ac->key);
free(ac);
ae->ajchannels--;
return 1;
}
static int clearAjoinChans(AjoinEntry *ae) {
AjoinChan *ac = NULL, *next = NULL;
if (!ae)
return 0;
for (ac = ae->channels; ac; ac = next) {
if (ac->next)
next = ac->next;
else
next = NULL;
if (ac->channel)
free(ac->channel);
if (ac->key)
free(ac->key);
free(ac);
}
ae->channels = NULL;
ae->ajchannels = 0;
return 1;
}
static int numberAjoinChans(AjoinEntry *ae) {
AjoinChan *current;
int nr = 1;
if (!ae)
return 0;
for (current = ae->channels; current; current = current->next) {
current->nr = nr++;
}
return 1;
}
/* ------------------------------------------------------------------------------- */
static AjoinEntry *createAjoinEntry(NickCore *nc) {
AjoinEntry *ae = NULL;
char buf[BUFSIZE];
if (!nc)
return NULL;
if ((ae = malloc(sizeof(AjoinEntry))) == NULL) {
fatal("Out Of Memory!");
}
ae->prev = NULL;
ae->next = NULL;
ae->row = counter;
ae->ajchannels = 0;
ae->in_use = 0;
ae->flags = DefAjoinFlags;
ae->channels = NULL;
/* add it to the ajoinTable */
if (ajoinTable[counter] == NULL) {
ajoinTable[counter] = ae;
ae->col = 0;
} else {
int i = 0, last = -1;
AjoinEntry *t, *prev = NULL;
for (t = ajoinTable[counter]; t; t = t->next) {
if (t->col != last + 1)
break;
last = t->col;
i++;
prev = t;
}
ae->col = i;
if (prev) {
if (prev->next) {
ae->next = prev->next;
prev->next->prev = ae;
}
prev->next = ae;
}
ae->prev = prev;
}
/* add to the nc, so it can be addressed relative quickly */
snprintf(buf, BUFSIZE, "%d;%d", ae->row, ae->col);
moduleAddData(&nc->moduleData, ModDataKey, buf);
if (counter < 1023)
counter++;
else
counter = 0;
return ae;
}
AjoinEntry *getAjoinEntry(NickCore *nc) {
char *t, *x, *y;
int row, col;
AjoinEntry *ae = NULL;
if (!nc)
return NULL;
t = moduleGetData(&nc->moduleData, ModDataKey);
if (t == NULL) {
return NULL;
}
x = myStrGetToken(t, ';', 0);
y = myStrGetToken(t, ';', 1);
row = atoi(x);
col = atoi(y);
for (ae = ajoinTable[row]; ae; ae = ae->next) {
if (ae->col == col)
break;
}
free(t);
free(x);
free(y);
return ae;
}
static int deleteAjoinEntry(AjoinEntry *ae) {
if (!ae)
return 0;
if (ajoinTable[ae->row] == ae)
ajoinTable[ae->row] = ae->next;
if (ae->prev)
ae->prev->next = ae->next;
if (ae->next)
ae->next->prev = ae->prev;
clearAjoinChans(ae);
ae->prev = NULL;
ae->next = NULL;
free(ae);
return 1;
}
/**
* When we are called, we assume used entries have in_use set to 1 (done while the
* DB is saved) and reset it to 0 for all other entries.
* Entries with in_use to 0 are free()'d and removed from the AjoinTable since we
* assume all other references to it have allready been removed.
**/
void freeUnusedEntries() {
AjoinEntry *next = NULL, *ae = NULL;
int i;
for (i = 0; i < 1024; i++) {
for (ae = ajoinTable[i]; ae; ae = next) {
if (ae->in_use == 1) {
/* Entry is still being used... resetting */
ae->in_use = 0;
next = ae->next;
continue;
}
/* Entry is no longer being used... free it */
next = ae->next;
deleteAjoinEntry(ae);
}
}
}
/* ------------------------------------------------------------------------------- */
/**
* When anope saves her databases, we do the same.
**/
int do_save(int argc, char **argv) {
if ((argc >= 1) && (!stricmp(argv[0], EVENT_STOP)))
save_ajoin_db();
return MOD_CONT;
}
/**
* When anope backs her databases up, we do the same.
**/
int db_backup(int argc, char **argv) {
if ((argc >= 1) && (!stricmp(argv[0], EVENT_STOP))) {
alog("[ns_ajoin] Backing up AJOIN database...");
ModuleDatabaseBackup(AJoinDB);
/* When anope backs her databases up, we also clear unused entries from the AjoinTable */
freeUnusedEntries();
}
return MOD_CONT;
}
/**************************************************************************
* DataBase Handling
**************************************************************************/
void load_ajoin_db(void) {
DBFile *dbptr = scalloc(1, sizeof(DBFile));
AjoinEntry *ae = NULL;
NickCore *nc = NULL;
char *key, *value;
int retval = 0;
fill_db_ptr(dbptr, 0, AJOINDBVERSION, s_NickServ, AJoinDB);
/* let's remove existing temp files here, because we only load dbs on startup */
remove(dbptr->temp_name);
/* Open the db, fill the rest of dbptr and allocate memory for key and value */
if (new_open_db_read(dbptr, &key, &value)) {
free(dbptr);
return; /* Bang, an error occurred */
}
while (1) {
/* read a new entry and fill key and value with it -Certus */
retval = new_read_db_entry(&key, &value, dbptr->fptr);
if (retval == DB_READ_ERROR) {
new_close_db(dbptr->fptr, &key, &value);
free(dbptr);
return;
} else if (retval == DB_EOF_ERROR) {
new_close_db(dbptr->fptr, &key, &value);
free(dbptr);
return;
} else if (retval == DB_READ_BLOCKEND) { /* DB_READ_BLOCKEND */
/* prevent chans from one block ending up in another... */
ae = NULL;
} else { /* DB_READ_SUCCESS */
if (!*key || !*value)
continue;
/* nick */
if (!stricmp(key, "ncd")) {
if ((nc = findcore(value))) {
ae = createAjoinEntry(nc);
} else
ae = NULL;
/* flags */
} else if (!stricmp(key, "flgs") && ae) {
ae->flags = (uint16) atoi(value);
/* channel entry */
} else if (!stricmp(key, "che") && ae) {
char *chan = myStrGetToken(value, ' ', 0);
char *chkey = myStrGetToken(value, ' ', 1);
addAjoinChan(ae, chan, chkey);
free(chan);
if (chkey)
free(chkey);
} else if (!stricmp(key, "AJOIN DB VERSION")) {
if ((int)atoi(value) != AJOINDBVERSION) {
alog("[\002ns_ajoin\002] Database version does not match any database versions supported by this module.");
alog("[\002ns_ajoin\002] Continuing with clean database...");
break;
}
}
} /* else */
} /* while */
free(dbptr);
}
void save_ajoin_db(void) {
DBFile *dbptr = scalloc(1, sizeof(DBFile));
NickCore *nc;
int i;
fill_db_ptr(dbptr, 0, AJOINDBVERSION, s_NickServ, AJoinDB);
/* time to backup the old db */
rename(AJoinDB, dbptr->temp_name);
if (new_open_db_write(dbptr)) {
rename(dbptr->temp_name, AJoinDB);
free(dbptr);
return; /* Bang, an error occurred */
}
/* Store the version of the DB in the DB as well...
* This will make stuff a lot easier if the database scheme needs to modified. */
new_write_db_entry("AJOIN DB VERSION", dbptr, "%d", AJOINDBVERSION);
new_write_db_endofblock(dbptr);
/* Go over the entire NickCore list and check whether each one has an ajoin entry */
for (i = 0; i < 1024; i++) {
for (nc = nclists[i]; nc; nc = nc->next) {
AjoinEntry *ae = NULL;
AjoinChan *ac = NULL;
ae = getAjoinEntry(nc);
if (!ae)
continue;
/* If there are no entries in the list and the configuration is set to the default
* value, there is no need to save it to the db. */
if (ae->ajchannels == 0 && ae->flags == DefAjoinFlags) {
/* Delete the entry first since it s just eating up space... */
moduleDelData(&nc->moduleData, ModDataKey);
deleteAjoinEntry(ae);
continue;
}
new_write_db_entry("ncd", dbptr, "%s", nc->display);
new_write_db_entry("flgs", dbptr, "%d", ae->flags);
if (ae->ajchannels > 0) {
for (ac = ae->channels; ac; ac = ac->next) {
new_write_db_entry("che", dbptr, "%s %s", ac->channel, (ac->key ? ac->key : ""));
}
}
new_write_db_endofblock(dbptr);
/* Marking the entry as in use... */
ae->in_use = 1;
}
}
if (dbptr) {
new_close_db(dbptr->fptr, NULL, NULL); /* close file */
remove(dbptr->temp_name); /* saved successfully, no need to keep the old one */
free(dbptr); /* free the db struct */
}
}
/* ------------------------------------------------------------------------------- */
/**
* We check if the ircd is supported
**/
int valid_ircd(void) {
if (!stricmp(IRCDModule, "unreal31"))
return 1;
if (!stricmp(IRCDModule, "unreal32"))
return 1;
if (!stricmp(IRCDModule, "viagra"))
return 1;
if (!stricmp(IRCDModule, "ptlink"))
return 1;
if (!stricmp(IRCDModule, "ultimate2"))
return 1;
if (!stricmp(IRCDModule, "plexus3"))
return 1;
if (!stricmp(IRCDModule, "inspircd10"))
return 1;
if (!stricmp(IRCDModule, "inspircd11"))
return 1;
return 0;
}
/* ------------------------------------------------------------------------------- */
/**
* (Re)Load the configuration directives
**/
void load_config(void) {
int i;
Directive confvalues[][1] = {
{{"AJoinDB", {{PARAM_STRING, PARAM_RELOAD, &AJoinDB}}}},
};
if (AJoinDB)
free(AJoinDB);
AJoinDB = NULL;
for (i = 0; i < 1; i++)
moduleGetConfigDirective(confvalues[i]);
if (!AJoinDB)
AJoinDB = sstrdup(DefAJoinDB);
if (debug)
alog ("[ns_ajoin] debug: AJoinDB set to %s", AJoinDB);
}
/**
* Upon /os reload call the routines for reloading the configuration directives
**/
int reload_config(int argc, char **argv) {
if (argc >= 1) {
if (!stricmp(argv[0], EVENT_START)) {
alog("[ns_ajoin]: Reloading configuration directives...");
load_config();
}
}
return MOD_CONT;
}
/* ------------------------------------------------------------------------------- */
/**
* Add language strings to the module's language db.
**/
void add_languages(void) {
char *langtable_en_us[] = {
/* LANG_AJOIN_DESC */
" AJOIN Manipulate the AutoJoin list.",
/* LANG_AJOIN_SYNTAX */
" Syntax: AJOIN { ADD | DEL | LIST | CLEAR } [\037channel\037 | \037entry-list\037] [\037key\037]",
/* LANG_AJOIN_SYNTAX_EXT */
" Syntax: \002AJOIN ADD \037channel\037 \037key\037\002 \n"
" \002AJOIN DEL {\037channel\037 | \037entry-nr\037 | \037list\037}\002\n"
" \002AJOIN LIST [\037mask\037 | \037list\037]\002 \n"
" \002AJOIN CLEAR\002 \n"
" \n"
" Maintains the \002AutoJoin list\002 for nick group. \n"
" If a user identifies to his nickname, he will \n"
" automatically join the listed channels. \n"
" \n"
" The \002AJOIN ADD\002 command adds the given channel\n"
" to the AutoJoin list or updates the existing entry, \n"
" if it is allready present on the AutoJoin list. \n"
" If no key is given and an entry for the channel is already\n"
" present, the key allready set (if any) will be unset. \n"
" Only \002registered channels\002 are accepted! \n"
" \n"
" The \002AJOIN DEL\002 command removes the given channel \n"
" from the AutoJoin list. If a list of entry numbers is given,\n"
" those entries are deleted.\n"
" \n"
" The \002AJOIN LIST\002 command displays the AutoJoin list.\n"
" If a wildcard mask is given, only those entries matching the\n"
" mask are displayed. If a list of entry numbers is given, only\n"
" those entries are shown.\n"
" \n"
" The \002AJOIN CLEAR\002 command clears all entries on the \n"
" AutoJoin list.",
/* LANG_AJOIN_DISABLED */
" Sorry, AutoJoin list modification is temporarily disabled.",
/* LANG_NO_LOCAL_CHAN */
" Cannot add local channels to ajoin list.",
/* LANG_CHAN_SYMB_REQUIRED */
" The channel symbol '\002#\002' is required.",
/* LANG_CHAN_UPDATED */
" Ajoin entry updated.",
/* LANG_AJOIN_LIST_FULL */
" The AutoJoin list is full.",
/* LANG_CHAN_ADDED */
" \002%s\002 has been added to the ajoin list.",
/* LANG_NO_AJOINS */
" No AutoJoins have been set.",
/* LANG_AJOIN_LIST_EMPTY */
" The AutoJoins list is empty.",
/* LANG_NO_ENTRY */
" \002%s\002 is not in the AutoJoin list.",
/* LANG_CHAN_DELETED */
" \002%s\002 has been deleted from the AutoJoin list.",
/* LANG_AJOIN_ENTRY */
" %d - Channel: %s - Key: %s",
/* LANG_AJOIN_ENTRIES */
" Found %d entries",
/* LANG_AJOIN_LIST_CLEARED */
" AJOIN list cleared.",
/* LANG_UNKWN_AJOIN_OPTION */
" Unknown AJOIN option.",
/* LANG_AJOINING */
" Automatically joining %s .",
/* LANG_AJOINING_FAILED */
" Could not automatically join %s .",
/* LANG_SET_AJOIN_DESC */
" \n"
" Commands Provided by module ns_ajoin: \n"
" AJOIN Change your autojoin settings.",
/* LANG_SET_AJOIN_SYNTAX */
" Syntax: SET AJOIN { ON | OFF | SILENT }",
/* LANG_SET_AJOIN_SYNTAX_EXT */
" Syntax: \002SET AJOIN { ON | OFF | SILENT }\002 \n"
" \n"
" Toggles whether or not your AutoJoin list is performed when \n"
" you IDENTIFY or UPDATE.\n"
" When set to ON, you will receive a notification for each channel\n"
" you are automatically joined to. When set to SILENT, you will receive\n"
" one message indicating you are automatically joining channels. \n"
" When set to OFF, you will not join any channels upon identifying.\n"
" \n"
" The default setting is \002ON\002.",
/* LANG_SET_AJOIN_ON */
" AutoJoin is now \002ON\002.",
/* LANG_SET_AJOIN_SILENT */
" AutoJoin is now \002ON\002 and set to \002SILENT\002 mode.",
/* LANG_SET_AJOIN_OFF */
" AutoJoin is now \002OFF\002.",
/* LANG_AJOINING_SUM_SUCCESS */
" Successfully joined %d channel(s).",
/* LANG_AJOINING_SUM_FAILED */
" Failed joining %d channel(s).",
/* LANG_AJOIN_DELETED_NR_1 */
" Deleted %d entry from the AutoJoin list.",
/* LANG_AJOIN_DELETED_NR */
" Deleted %d entries from the AutoJoin list.",
};
char *langtable_nl[] = {
/* LANG_AJOIN_DESC */
" AJOIN Beheer de autojoin lijst.",
/* LANG_AJOIN_SYNTAX */
" Gebruik: AJOIN {ADD | DEL | LIST | CLEAR} [\037kanaal\037 | \037lijst\037] [\037wachtwoord\037]",
/* LANG_AJOIN_SYNTAX_EXT */
" Gebruik: \002AJOIN ADD \037kanaal\037 \037wachtwoord\037\002 \n"
" \002AJOIN DEL {\037kanaal\037 | \037nummer\037 | \037lijst\037}\002 \n"
" \002AJOIN LIST [\037mask\037 | \037lijst\037]\002 \n"
" \002AJOIN CLEAR\002 \n"
" \n"
" Onderhoud de \002AutoJoin lijst\002 voor een nick groep. \n"
" Als een gebruiker zich voor zijn nickname identificeerd,\n"
" zal hij automatisch de kanalen in de lijst joinen.\n"
" \n"
" Het \002AJOIN ADD\002 commando voegt het gegeven kanaal toe \n"
" aan de AutoJoin lijst of werkt de bestaande entry bij, \n"
" indien het kanaal reeds aanwezig is op de AutoJoin lijst. \n"
" Als er geen wachtwoord is opgegeven en het kanaal reeds \n"
" aanwezig is op de lijst, zal het wachtwoord dat reeds is \n"
" ingesteld (indien aanwezig) verwijderd worden. \n"
" Enkel \002geregistreerde kanalen\002 worden aanvaard!. \n"
" \n"
" Het \002AJOIN DEL\002 commando verwiderd het gegeven kanaal \n"
" van de AutoJoin lijst. Als een lijst met entry-nummers is\n"
" gegeven, worden deze verwijderd.\n"
" \n"
" Het \002AJOIN LIST\002 commando geeft de AutoJoin lijst weer.\n"
" Als een wildcard mask is opgegeven worden alleen de overeenkomsten\n"
" weergegeven. Als een lijst van entry-nummers in gegeven wordt,\n"
" worden alleen die weergegeven\n"
" \n"
" Het \002AJOIN CLEAR\002 commando maakt de AutoJoin lilst leeg.",
/* LANG_AJOIN_DISABLED */
" Sorry,het wijzigen van de AutoJoin lijst is tijdelijk uitgeschakeld.",
/* LANG_NO_LOCAL_CHAN */
" Kan geen lokale kanalen toevoegen aan de ajoin list.",
/* LANG_CHAN_SYMB_REQUIRED */
" Het kanaal symbool '\002#\002' is vereist.",
/* LANG_CHAN_UPDATED */
" AutoJoin entry bijgewerkt.",
/* LANG_AJOIN_LIST_FULL */
" De AutoJoin lijst is vol.",
/* LANG_CHAN_ADDED */
" \002%s\002 is aan de autojoin lijst toegevoegd.",
/* LANG_NO_AJOINS */
" Er zijn geen AutoJoins ingesteld.",
/* LANG_AJOIN_LIST_EMPTY */
" De AutoJoin lijst is leeg.",
/* LANG_NO_ENTRY */
" \002%s\002 bestaat niet in de AutoJoin lijst.",
/* LANG_CHAN_DELETED */
" \002%s\002 is van de AutoJoin lijst verwijderd.",
/* LANG_AJOIN_ENTRY */
" %d - Kanaal: %s - Wachtwoord: %s",
/* LANG_AJOIN_ENTRIES */
" %d plaatsen gevonden.",
/* LANG_AJOIN_LIST_CLEARED */
" AutoJoin lijst is leeggemaakt.",
/* LANG_UNKWN_AJOIN_OPTION */
" Onbekende AJOIN parameter.",
/* LANG_AJOINING */
" Automatisch %s joinen",
/* LANG_AJOINING_FAILED */
" Kon %s niet automatisch joinen.",
/* LANG_SET_AJOIN_DESC */
" \n"
" Commandos toegevoegd door module ns_ajoin: \n"
" AJOIN Wijzig de instellingen van je autojoin.",
/* LANG_SET_AJOIN_SYNTAX */
" Gebruik: SET AJOIN { ON | OFF | SILENT }",
/* LANG_SET_AJOIN_SYNTAX_EXT */
" Gebruik: \002SET AJOIN { ON | OFF | SILENT }\002 \n"
" \n"
" Configureert of je AutoJoin lijst al dan niet wordt uitgevoerd bij \n"
" een nickserv IDENTIFY of UPDATE.\n"
" Wanneer deze optie op ON staat zal je een melding krijgen voor elk kanaal\n"
" dat je automatisch joint. Wanneer het op SILENT staat, krijg je één melding\n"
" die aangeeft dat je automatisch kanalen joint. \n"
" Wanneer deze optie op OFF staat zal je niet automatisch kanalen joinen.\n"
" \n"
" De standaard waarde is \002ON\002.",
/* LANG_SET_AJOIN_ON */
" AutoJoin staat nu \002OAAN\002.",
/* LANG_SET_AJOIN_SILENT */
" AutoJoin staat nu \002AAN\002 in \002STILLE\002 modus.",
/* LANG_SET_AJOIN_OFF */
" AutoJoin staat nu \002UIT\002.",
/* LANG_AJOINING_SUM_SUCCESS */
" Succesvol %d kanaal/kanalen gejoint.",
/* LANG_AJOINING_SUM_FAILED */
" Kon %d kanaal/kanalen niet joinen.",
/* LANG_AJOIN_DELETED_NR_1 */
" %d kanaal uit de AutoJoin lijst verwijderd.",
/* LANG_AJOIN_DELETED_NR */
" %d kanalen uit de AutoJoin lijst verwijderd.",
};
char *langtable_de[] = {
/* LANG_AJOIN_DESC */
" AJOIN AutoJoin-Liste bearbeiten.",
/* LANG_AJOIN_SYNTAX */
" Syntax: AJOIN { ADD | DEL | LIST | CLEAR } [\037channel\037 | \037entry-list\037] [\037key\037]",
/* LANG_AJOIN_SYNTAX_EXT */
" Syntax: \002AJOIN ADD \037channel\037 \037key\037\002 \n"
" \002AJOIN DEL {\037channel\037 | \037entry-nr\037 | \037list\037}\002 \n"
" \002AJOIN LIST [\037mask\037 | \037list\037]\002 \n"
" \002AJOIN CLEAR\002 \n"
" \n"
" Verwaltet die \002AutoJoin Liste\002 für eine Nick-Gruppe. \n"
" Wenn ein user sich mit seinem Nicknamen identifiziert, so \n"
" betritt er automatisch die Räume aus der Liste. \n"
" \n"
" \002AJOIN ADD\002 fügt den angegebenen Raum \n"
" der AutoJoin-Liste hinzu oder aktualisiert den \n"
" Eintrag, wenn der angegebene Raum schon vorhanden ist. \n"
" Wenn kein Raum-Schlüssel angegeben wird und es bereits \n"
" einen Listeneintrag für diesen Raum gibt, wo ein Schlüssel \n"
" angegeben wurde, dann wird dieser Schlüssel zurückgesetzt. \n"
" Nur \002registrierte Räume\002 sind erlaubt! \n"
" \n"
" \002AJOIN DEL\002 entfernt den angegebenen Raum \n"
" aus der AutoJoin-Liste. Wurden die Nummern der Einträge angegeben,\n"
" werden die dazugehörigen Einträge gelöscht.\n"
" \n"
" \002AJOIN LIST\002 zeigt die AutoJoin-liste.\n"
" If a wildcard mask is given, only those entries matching the\n"
" mask are displayed. Wurden die Nummern der Listeneinträge angegeben,\n"
" werden nur diese Einträge angezeigt.\n"
" \n"
" \002AJOIN CLEAR\002 löscht alle Einträge aus der \n"
" AutoJoin-Liste.",
/* LANG_AJOIN_DISABLED */
" Sorry, das Bearbeiten der AutoJoin-Liste ist vorrübergehend deaktiviert.",
/* LANG_NO_LOCAL_CHAN */
" Kann keine lokalen Räume zur AutoJoin-Liste hinzufügen.",
/* LANG_CHAN_SYMB_REQUIRED */
" Das Raumsymbol '\002#\002' wird benötigt.",
/* LANG_CHAN_UPDATED */
" AutoJoin Eintrag aktualisiert.",
/* LANG_AJOIN_LIST_FULL */
" Die AutoJoin-Liste ist voll.",
/* LANG_CHAN_ADDED */
" \002%s\002 wurde zur AutoJoin-Liste hinzugefügt.",
/* LANG_NO_AJOINS */
" Es wurden noch keine AutoJoins festgelegt.",
/* LANG_AJOIN_LIST_EMPTY */
" Die AutoJoin-Liste ist leer.",
/* LANG_NO_ENTRY */
" \002%s\002 ist nicht in der AutoJoin-liste.",
/* LANG_CHAN_DELETED */
" \002%s\002 wurde aus der AutoJoin-Liste gelöscht.",
/* LANG_AJOIN_ENTRY */
" %d - Channel: %s - Key: %s",
/* LANG_AJOIN_ENTRIES */
" Habe %d Einträge gefunden ",
/* LANG_AJOIN_LIST_CLEARED */
" AutoJoin-Liste gesäubert.",
/* LANG_UNKWN_AJOIN_OPTION */
" Unbekannte AutoJoin-Option.",
/* LANG_AJOINING */
" AutoJoining %s .",
/* LANG_AJOINING_FAILED */
" Konnte %s nicht AutoJoinen .",
/* LANG_SET_AJOIN_DESC */
" \n"
" Unterstützte Kommandos von Modul ns_ajoin: \n"
" AJOIN AutoJoin-Settings ändern.",
/* LANG_SET_AJOIN_SYNTAX */
" Syntax: SET AJOIN { ON | OFF | SILENT }",
/* LANG_SET_AJOIN_SYNTAX_EXT */
" Syntax: \002SET AJOIN { ON | OFF | SILENT }\002 \n"
" \n"
" Stellt ein, ob die AutoJoin-Liste abgearbeitet wird, wenn \n"
" IDENTIFY oder UPDATE ausgeführt wird.\n"
" Wenn ON eingestellt wird, bekommst du für jeden Raum, den du AutoJoinst \n"
" eine Nachricht. Bei Einstellung SILENT, bekommst du \n"
" eine Nachricht, welche dir mitteilt, dass du AutoJoinst. \n"
" Die Einstellung OFF bewirkt, dass du keinen Raum beim IDENTIFY Befehl betrittst. \n"
" \n"
" Voreinstellung ist: \002ON\002.",
/* LANG_SET_AJOIN_ON */
" AutoJoin ist jetzt \002ON\002.",
/* LANG_SET_AJOIN_SILENT */
" AutoJoin ist jetzt \002ON\002 und auf \002SILENT\002 eingestellt.",
/* LANG_SET_AJOIN_OFF */
" AutoJoin ist jetzt \002OFF\002.",
/* LANG_AJOINING_SUM_SUCCESS */
" Erfolgreich %d Channel(s) automatisch betreten.",
/* LANG_AJOINING_SUM_FAILED */
" Fehler beim AutoJoinen von %d channel(s).",
/* LANG_AJOIN_DELETED_NR_1 */
" %d Eintrag wurde aus der AutoJoin-Liste entfernt.",
/* LANG_AJOIN_DELETED_NR */
" %d Einträge wurden aus der AutoJoin-Liste entfernt.",
};
char *langtable_tr[] = {
/* LANG_AJOIN_DESC */
" AJOIN Otojoin listesinizi yönetebilirsiniz.",
/* LANG_AJOIN_SYNTAX */
" Kullanýmý: AJOIN { ADD | DEL | LIST | CLEAR } [\037#kanaladi\037 | \037entry-list\037] [\037anahtar\037]",
/* LANG_AJOIN_SYNTAX_EXT */
" Kullanýmý: \002AJOIN ADD \037#kanaladi\037 \037anahtar\037\002 \n"
" \002AJOIN DEL {\037#kanaladi\037 | \037entry-nr\037 | \037list\037}\002 \n"
" \002AJOIN LIST [\037mask\037 | \037list\037]\002 \n"
" \002AJOIN CLEAR\002 \n"
" \n"
" Nick grubunuzun \002otojoin list\002 düzenini görüntüler. \n"
" Nickinizi identify ettikten sonra, otomatik \n"
" olarak eklediðiniz kanallara giriþ yaparsýnýz. \n"
" \n"
" Otojoin listeniz hazýrda var ise \002AJOIN ADD\002 komutu\n"
" ile kanal ekleyebilir veya otojoin \n"
" listesini düzenlersiniz. \n"
" Eðer kanal eklerken anahtar belirtilmediyse ve kanalda \n"
" anahtar var ise, kanala girmek için anahtar olmucak.\n"
" Sadece \002kayýtlý kanal\002 kabul edilir! \n"
" \n"
" \002AJOIN DEL\002 komutu eklediðiniz kanalý otojoin \n"
" listesinden çýkartýr. Eðerki listede ki giriþ numaralarý verilirse,\n"
" bu giriþler silinir.\n"
" \n"
" \002AJOIN LIST\002 komutu otojoin listenizi görüntüler.\n"
" Eðerki mask belirtilirse, sadece belirtrilen bu masklar\n"
" görüntülenir. Eðerki sadece listede ki giriþ numaralarý belirtilirse, sadece\n"
" bunlar gözükür.\n"
" \n"
" \002AJOIN CLEAR\002 komutu bütün ekli olan kanallarýnýzý \n"
" otojoin listenizden siler.",
/* LANG_AJOIN_DISABLED */
" Üzgünüm, ajoin komutu kullaným dýþý býrakýlmýþtýr.",
/* LANG_NO_LOCAL_CHAN */
" Lokal kanal ekleyemezsiniz.",
/* LANG_CHAN_SYMB_REQUIRED */
" '\002#\002' iþaretini kulanmanýz gerekmektedir.",
/* LANG_CHAN_UPDATED */
" Otojoin giriþleriniz güncellendi.",
/* LANG_AJOIN_LIST_FULL */
" Otojoin listiniz dolmuþtur.",
/* LANG_CHAN_ADDED */
" \002%s\002 kanali otojoin listenize eklendi.",
/* LANG_NO_AJOINS */
" Herhangi bir otojoin kullanýlmadý.",
/* LANG_AJOIN_LIST_EMPTY */
" Otojoin listiniz boþ.",
/* LANG_NO_ENTRY */
" \002%s\002 kanali otojoin listenizde deðildir.",
/* LANG_CHAN_DELETED */
" \002%s\002 kanali otojoin listenizden silindi.",
/* LANG_AJOIN_ENTRY */
" %d - Kanaladi: %s - Anahtar: %s",
/* LANG_AJOIN_ENTRIES */
" %d giriþ bulundu",
/* LANG_AJOIN_LIST_CLEARED */
" Otojoin listeniz temizlendi.",
/* LANG_UNKWN_AJOIN_OPTION */
" Bilinmeyen otojoin komutu.",
/* LANG_AJOINING */
" %s kanalýna otomatik olarak giriþ yaptýnýz .",
/* LANG_AJOINING_FAILED */
" %s kanalýna otomatik giriþ yapamadýnýz .",
/* LANG_SET_AJOIN_DESC */
" \n"
" ns_ajoin modulu hakkýnda: \n"
" AJOIN Otojoin ayarlarýnýzý düzenler.",
/* LANG_SET_AJOIN_SYNTAX */
" Kullanýmý: SET AJOIN { ON | OFF | SILENT }",
/* LANG_SET_AJOIN_SYNTAX_EXT */
" Kullanýmý: \002SET AJOIN { ON | OFF | SILENT }\002 \n"
" \n"
" Otojoin listiniz hakkýnda IDENTIFY ve UPDATE edildiginde \n"
" geniþ bilgi sunar.\n"
" ON olduðunda otojoin listesinizdeki kanallara giriþ\n"
" yapmanýzý saðlar. Bu ayar SILENT olduðunda, sadece bir tane\n"
" otomatik giriþ yaptýðýnýz kanallardan giriþ mesajýnýz alýrsýnýz. \n"
" Bu ayar OFF olduðunda, hiçbir kanala otojoin yapamazsýnýz.\n"
" \n"
" Belirlenmiþ ayar \002ON\002.",
/* LANG_SET_AJOIN_ON */
" Þuan otojoin \002ON\002.",
/* LANG_SET_AJOIN_SILENT */
" Otojoin þuan \002ON\002 ve \002SILENT\002 modundadýr.",
/* LANG_SET_AJOIN_OFF */
" Otojoin þuan \002OFF\002.",
/* LANG_AJOINING_SUM_SUCCESS */
" %d kanallarýna baþarýlý þekilde giriþ yaptýnýz.",
/* LANG_AJOINING_SUM_FAILED */
" %d kanallarýna giriþ yapamadýnýz.",
/* LANG_AJOIN_DELETED_NR_1 */
" %d giriþ otojoin listesinden silindi.",
/* LANG_AJOIN_DELETED_NR */
" %d giriþler otojoin listesinden silindi.",
};
char *langtable_ru[] = {
/* LANG_AJOIN_DESC */
" AJOIN Óïðàâëÿåò AJOIN-ñïèñêîì (àâòîçàõîä).",
/* LANG_AJOIN_SYNTAX */
"Ñèíòàêñèñ: AJOIN { ADD | DEL | LIST | CLEAR } [\037#êàíàë\037 | \037ñïèñîê_çàïèñåé\037] [\037êëþ÷\037]",
/* LANG_AJOIN_SYNTAX_EXT */
"Ñèíòàêñèñ: \002AJOIN ADD \037#êàíàë\037 \037êëþ÷\037\002 \n"
" \002AJOIN DEL {\037#êàíàë\037 | \037íîìåð_çàïèñè\037 | \037ñïèñîê_çàïèñåé\037}\002\n"
" \002AJOIN LIST [\037ìàñêà\037 | \037ñïèñîê\037]\002 \n"
" \002AJOIN CLEAR\002 \n"
" \n"
"Ïîçâîëÿåò óïðàâëÿòü ñïèñêîì \002àâòîçàõîäà\002 íà êàíàëû. \n"
"Êàê òîëüêî ïîëüçîâàòåëü èäåíòèôèöèðóåòñÿ ê ñâîåìó íèêó, îí áóäåò \n"
"àâòîìàòè÷åñêè ïðèñîåäèíåí ê óêàçàííûì â ñïèñêå êàíàëàì. \n"
" \n"
"Êîìàíäà \002AJOIN ADD\002 ïîçâîëÿåò äîáàâèòü êàíàë â ñïèñîê \n"
"àâòîçàõîäà èëè îáíîâèòü èíôîðìàöèþ äëÿ óæå ñóùåñòâóþùåãî â \n"
"â ñïèñêå êàíàëà. \n"
"Åñëè ïðè îáíîâëåíèè çàïèñè äëÿ êàêîãî ëèáî êàíàëà, âû íå óêàæåòå \n"
"äîïîëíèòåëüíûé ïàðàìåòð 'êëþ÷' - òåêóùèé óñòàíîâëåííûé äëÿ êàíàëà \n"
"êëþ÷ áóäåò óäàëåí (ïðè óñëîâèè ÷òî îí ñòîÿë, êîíå÷íî) \n"
"Ñïèñîê àâòîçàõîäà ìîæåò ñîäåðæàòü òîëüêî \002çàðåãèñòðèðîâàííûå êàíàëû\002 \n"
" \n"
"Êîìàíäà \002AJOIN DEL\002 ïîçâîëÿåò óäàëèòü óêàçàííûé êàíàë èç \n"
"ñïèñêà àâòîçàõîäà.  êà÷åñòâå ïàðàìåòðà âû ìîæåòå óêàçàòü ñïèñîê \n"
"çàïèñåé èëè êîíêðåòíûé íîìåð çàïèñè ajoin-ñïèñêà \n"
" \n"
"Êîìàíäà \002AJOIN LIST\002 îòîáðàæàåò òåêóùèé ñïèñîê àâòîçàõîäà.\n"
"Äîïóñòèìî èñïîëüçîâàíèå ïîäñòàíîâî÷íûõ ñèìâîëîâ (ãëîáàëüíûõ ìàñîê)\n"
"â êà÷åñòâå äîïîëíèòåëüíîãî ïàðàìåòðà. Òàê æå, â êà÷åñòâå óëîâèÿ ëèñòèíãà\n"
"êàíàëîâ, âû ìîæåòå óêàçàòü ñïèñîê çàïèñåé.\n"
" \n"
"Êîìàíäà \002AJOIN CLEAR\002 ïîëíîñòüþ óäàëÿåò \002âñå\002 çàïèñè èç \n"
"ñïèñêà àâòîçàõîäà.",
/* LANG_AJOIN_DISABLED */
"Èçâèíèòå, íî ìîäèôèêàöèÿ ñïèñêà àâòîçàõîäà âðåìåííî íåäîñòóïíà.",
/* LANG_NO_LOCAL_CHAN */
"Âû íå ìîæåòå äîáàâèòü ëîêàëüíûå êàíàëû â ñïèñîê àâòîçàõîäà.",
/* LANG_CHAN_SYMB_REQUIRED */
"Âû äîëæíû óêàçàòü ñèìâîë êàíàëà: '\002#\002' .",
/* LANG_CHAN_UPDATED */
"Îáíîâëåíà îäíà çàïèñü â ñïèñêå àâòîçàõîäà.",
/* LANG_AJOIN_LIST_FULL */
"Ñïèñîê àâòîçàõîäà ïåðåïîëíåí.",
/* LANG_CHAN_ADDED */
"Êàíàë \002%s\002 óñïåøíî äîáàâëåí â ñïèñîê àâòîçàõîäà.",
/* LANG_NO_AJOINS */
"Ñïèñîê àâòîçàõîäà äëÿ âàøåãî íèêà íå íàñòðîåí.",
/* LANG_AJOIN_LIST_EMPTY */
"Ñïèñîê àâòîçàõîäà ïóñò.",
/* LANG_NO_ENTRY */
"Êàíàë \002%s\002 íå ïðèñóòñòâóåò â ñïèñêå àâòîçàõîäà.",
/* LANG_CHAN_DELETED */
"Êàíàë \002%s\002 óñïåøíî óäàëåí èç ñïèñêà àâòîçàõîäà.",
/* LANG_AJOIN_ENTRY */
" %d - Êàíàë: %s - Êëþ÷: %s",
/* LANG_AJOIN_ENTRIES */
"Âñåãî çàïèñåé: %d",
/* LANG_AJOIN_LIST_CLEARED */
"Ñïèñîê àâòîçàõîäà ïîëíîñòüþ î÷èùåí.",
/* LANG_UNKWN_AJOIN_OPTION */
"Íåèçâåñòíàÿ AJOIN-îïöèÿ.",
/* LANG_AJOINING */
"Óñïåøíûé àâòîçàõîä íà %s .",
/* LANG_AJOINING_FAILED */
"Íåóäà÷íûé àâòîçàõîä íà %s .",
/* LANG_SET_AJOIN_DESC */
" \n"
"Êîìàíäû, ïðåäîñòàâëÿåìûå ìîäóëåì ns_ajoin: \n"
" AJOIN Ïîçâîëÿåò íàñòðîèòü ðåæèì àâòîçàõîäà.",
/* LANG_SET_AJOIN_SYNTAX */
"Ñèíòàêñèñ: SET AJOIN { ON | OFF | SILENT }",
/* LANG_SET_AJOIN_SYNTAX_EXT */
"Ñèíòàêñèñ: \002SET AJOIN { ON | OFF | SILENT }\002 \n"
" \n"
"Ïîçâîëÿåò âêëþ÷èòü èëè âûêëþ÷èòü àâòîçàõîä íà êàíàëû êàê òîëüêî \n"
"âû èäåíòèôèöèðóåòåñü ê íèêó èëè èñïîëüçóåòå êîìàíäó UPDATE. \n"
"Óñòàíîâêà â ON àêòèâèðóåò àâòîìàòè÷åñêîå óâåäîìëåíèå îá àâòîçàõîäå íà \n"
"êàæäûé êàíàë èç âàøåãî ñïèñêà àâòîçàõîäà. Óñòàíîâêà â SILENT àêòèâèðóåò \n"
"ëèøü åäèíñòâåííîå óâåäîìëåíèå î òîì, ê êàêîìó êîëè÷åñòâó êàíàëîâ èç âàøåãî \n"
"ñïèñêà àâòîçàõîäà âû óñïåøíî/íåóñïåøíî ïðèñîåäèíåíû. \n"
"Óñòàíîâêà îïöèè â OFF îòêëþ÷èò âîçìîæíîñòü àâòîçàõîäà âîîáùå. \n"
" \n"
"Óñòàíîâêà ïî-óìîë÷àíèþ: \002ON\002.",
/* LANG_SET_AJOIN_ON */
"Ðåæèì àâòîçàõîäà \002ÂÊËÞ×ÅÍ\002.",
/* LANG_SET_AJOIN_SILENT */
"Ðåæèì àâòîçàõîäà \002ÂÊËÞ×ÅÍ\002, áóäåò èñïîëüçîâàí \002SILENT\002-ðåæèì (ìèíèìóì óâåäîìëåíèé).",
/* LANG_SET_AJOIN_OFF */
"Ðåæèì àâòîçàõîäà \002ÂÛÊËÞ×ÅÍ\002.",
/* LANG_AJOINING_SUM_SUCCESS */
"Óñïåøíûé àâòîçàõîä íà %d êàíàë(à/îâ).",
/* LANG_AJOINING_SUM_FAILED */
"Íåóäà÷íûé àâòîçàõîä íà %d êàíàë(à/îâ).",
/* LANG_AJOIN_DELETED_NR_1 */
"Çàïèñü ïîä íîìåðîì %d óäàëåíà èç ñïèñêà àâòîçàõîäà.",
/* LANG_AJOIN_DELETED_NR */
"Èç ñïèñêà àâòîçàõîäà óñïåøíî óäàëåíû %d çàïèñåé.",
};
moduleInsertLanguage(LANG_EN_US, LANG_NUM_STRINGS, langtable_en_us);
moduleInsertLanguage(LANG_NL, LANG_NUM_STRINGS, langtable_nl);
moduleInsertLanguage(LANG_DE, LANG_NUM_STRINGS, langtable_de);
moduleInsertLanguage(LANG_TR, LANG_NUM_STRINGS, langtable_tr);
moduleInsertLanguage(LANG_RU, LANG_NUM_STRINGS, langtable_ru);
}
/* EOF */
Tidak ada komentar:
Posting Komentar