"Ilmu pengetahuan Tertinggi adalah ilmu pengetahuan yang tidak bisa dipikirkan oleh otak manusia tapi bisa dirasakan hati manusia"

"Top science is science that can not be considered by the human brain can be felt but the human heart"

"トップ科学人間の脳考えることはできない科学感じることができる、人間のです."

Senin, 08 Agustus 2011

Anope ns_ajoin

/**
* -----------------------------------------------------------------------------
* 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