1
0
Fork 0
mirror of git://git.psyced.org/git/psyced synced 2024-08-15 03:25:10 +00:00

let the past begone in cvs land. welcome to igit igit!

This commit is contained in:
PSYC 2009-01-26 20:21:29 +01:00
commit 4e601cf1c7
509 changed files with 77963 additions and 0 deletions

392
world/net/irc/common.c Normal file
View file

@ -0,0 +1,392 @@
// $Id: common.c,v 1.107 2008/04/16 14:21:17 lynx Exp $ // vim:syntax=lpc
//
// common functions for IRC servers and clients (gateways)
//
#define MYNICK qName() // common for both server and user
#define NO_INHERIT
#include "irc.h"
//#include "hack.i"
#undef NO_INHERIT
#include "reply.h"
#include <strings.h>
#include <text.h>
virtual inherit NET_PATH "output";
volatile string prefix;
parse(a) {
string t, from, cmd, args, text;
#ifdef _flag_log_sockets_IRC
log_file("RAW_IRC", "%d %O\t<< %s\n", time(), ME, a);
#endif
IRCD( D("IRC< '"+a+"'\n"); )
// some clients like ircII like to send trailing spaces
// which may f**k up some parsing. also regular irc servers
// ignore leading spaces, so should we
#if __EFUN_DEFINED__(trim)
//if (a == "") return; // but first make sure there's something at all
//if (a[<1] == ' ' || a[0] == ' ')
a = trim(a, TRIM_BOTH);
// efun probably faster than doing all those checks anyway
#else
// not as efficient as it could be, but we have trim() anyway
if (a == "") return; // first make sure there's something at all
while (a[0] == ' ') a = a[1..];
while (a[<1] == ' ') a = a[0..<2];
#endif
if (a == "") return; // don't let " \n" execute "/s"
unless (sscanf(a, ":%s %s", from, t)) t = a;
sscanf(t, "%s :%s", t, text);
unless (sscanf(t, "%s %s", cmd, args)) cmd = t;
if (cmd) ircMsg(from, lower_case(cmd), args, text, a);
return 1;
}
ircMsg(from, cmd, args, text, all) {
mixed t,t1;
switch(cmd) {
case "motd":
motd();
return 1;
case "lusers":
lusers();
return 1;
case "time":
t = time();
t1 = ctime(t);
write(SERVER_SOURCE "391 " + MYNICK + " " SERVER_HOST " " +
t + "0 :" + isotime(t1, 0) + " -- " + hhmmss(t1) + "\n");
return 1;
case "ping":
// sieht doof aus scheint aber amtlich zu sein
// ne isses nich. man ponged, was der user gepinged hat.
// write(SERVER_SOURCE "PONG " SERVER_HOST " :"+ MYNICK +"\n");
// eine fantasievolle lösung:
write(SERVER_SOURCE "PONG " SERVER_HOST " :"
+( text || args || MYNICK )+"\n");
return 1;
case "quit":
quit();
return 1;
case "userhost":
// kleine clientverarsche
reply(RPL_USERHOST, ":"+args+"=+"+args+"@" SERVER_HOST);
return 1;
default:
// write("421 :Huh?\n");
// D(S("Unknown IRC: %s\n", all));
// log_file("IRCPROT", "[%s] %s\n", query_ip_number(), all);
// return 1;
return 0;
}
}
motd() {
string buffer = "";
// sollte alles in der textdb sein statt den reply hack zu verwenden
// ist aber leider ein bisschen tragisch, da sich ne motd abprubt ändern
// können soll... :( - die textdb geht ja eher von statischen inhalten aus.
// was halt ihr sinn ist.
//
// ok, also nicht die statische textdb - aber es gibt ja auch dynamische
// textdb sachen.. und ausserdem, MOTD sollte doch mehrsprachig sein können!?
emit(sreply(RPL_MOTDSTART, ":- " SERVER_HOST " Message of the Day - "));
#ifdef MOTD_FILE
if (file_size(MOTD_FILE) > 0) {
string motd_file;
// wir machen demnäxt noch n anderes define, dann kommts aus der
// datenbank. hat dann nur immernoch den beieffekt, dass in der datenbank
// direkt das MOTD-prefix stehen muss, und das werden ungefähr 3 leute
// wenn überhaupt verwenden, und alle davon aus dem kreis der muve/psycdevs.
// dummerweise fiel hier im Zuge des neuen emit das '- ' flach - fippo
P3(("MOTD_FILE (%O) found\n", MOTD_FILE))
motd_file = read_file(MOTD_FILE);
emit(sreply(RPL_MOTD, ":" + motd_file));
} else
#endif
emit(sreply(RPL_MOTD, ":- [PSYC] will probably make you happier. http://about.psyc.eu/\n"
#if 0
"- We're in public beta testing stage.\n"
"- Usage manuals and legacy transition help on http://help.pages.de/\n"
"- \n"
"- You're the administrator of this server? Save your customized\n"
#ifdef MOTD_FILE
"- MOTD to " MOTD_FILE " in psyced's world/ directory."
#else
"- MOTD to psyced's world/ directory and add\n"
"- #define MOTD_FILE \"<filename>\" to your local/local.h"
#endif
#endif
));
emit(sreply(RPL_ENDOFMOTD, ":End of MOTD command"));
return 1;
}
lusers() {
reply(RPL_LUSERCLIENT, ":There are " + amount_people()
+ " users on this server\n");
}
qCharset() {}
render(string mc, string data, mapping vars, mixed source) {
string template, output;
mixed t;
P0(("common:render %O %O\n", ME, data));
#if 1 // def IRCEXPERIMENTAL
template = T(mc, 0); // enable textdb inheritance
#else
template = T(mc, "");
#endif
<<<<<<< common.c
=======
#ifndef _flag_disable_stamp_time_IRC
t = vars["_time_place"] || vars["_time_log"];
if (t && v("timestamp") != "off" // && abbrev("_message", mc)
&& stringp(data)) {
if (stringp(t)) t = to_int(t); // we need types ;)
if (v("timestamp") == "on") {
string msa = " ";
msa[0] = 0x01; // msa's CTCP character
// should use psyctime instead of unixtime
data = msa +"TS "+ (t - PSYC_EPOCH) +msa+ data;
} else
data = "["+ hhmm(ctime(t)) +"] "+ data; // use T() ?
P3(("%O data is %O\n", ME, data))
}
#endif
>>>>>>> 1.107
P3(("c:r pre ptext: %O %O %O %O\n", template, vars, data, source ));
output = psyctext( template, vars, data, source);
P3(("c:r 1st ptext: %O\n", output));
if (!output || output=="") return D2(D("irc/user: empty output\n"));
#ifndef _flag_disable_stamp_time_IRC
if (t = vars["_time_place"] || vars["_time_log"]) {
PT(("%O got timestamp %O\n", ME, t))
}
if (t && v("timestamp") != "off" && abbrev("_message", mc)
&& stringp(output)) {
output = v("timestamp")
? output +" ["+ hhmm(ctime(t)) +"]" // use T() ?
: output + "%TS "+ hhmm(ctime(t)) +"%";
}
#endif
if (template == "") {
#ifdef PREFIXES
// der ganze prefix kram is im irc protokoll in der
// tat blödsinn.. muss ma anders machen..
if (abbrev("_prefix", mc)) return prefix = output+" ";
else
#endif
output += "\n";
}
if (output[0] == '#') output = SERVER_SOURCE + output[1..];
else if (output[0] != ':') {
string t2;
if (prefix) { output = prefix+output; prefix=0; }
// ich wage es mal hier den raumnamen als ziel auszugeben.
// wenn das nicht für alle fälle gut ist, dann mit dem
// if auf _notice_action einschränken. else liegt auch
// schon bereit. alles klar --lynX
// if (abbrev("_notice_action", mc)) {
// output = psyc2irc(mc) +" "+(vars["_nick_place"] ?
// place2channel(vars["_nick_place"]) : MYNICK)
// +" :" + output;
// else
// output = psyc2irc(mc) +" "+MYNICK+" :" + output;
// TODO: in theory, going thru the textdb twice should
// not be necessary, but it is the most straightforward
// solution
t2 = T(mc, data);
if (template != t2) {
output = psyctext( t2, vars, data, source) + "\n";
P3(("c:r 2nd ptext: %O\n", output));
#if DEBUG > 0
} else {
P3(("c:r %O==%O so there is no 2nd ptext for %O.\n"
"see also render(%O,%O,%O,%O)\n",
template, t2, output,
mc, data, vars, source));
#endif
}
unless (trail("\n", output)) {
PT(("(harmless) no IRC template for %O\n", mc))
log_file("IRC_TEXTDB", "%O\n", mc);
return;
}
// _silent: when casts from a conversation-filtered place
// arrive, which isnt known to the client as a channel, we
// revert to personal notices to not irritate it.
if (
#ifdef NOT_EXPERIMENTAL
// would be nicer to check if source is a place but
// isn't trivial right here.. looks like joining remote
// xmpp: mucs is affected by this change - you may not
// be able to see history. fippo is looking into this.
// <fippo> i wanted this for the _notice_place_* use case
// i would propose keeping the old way and adding a
// condition with _nick_place && abbrev(_notice_place, mc)
(vars["_nick_place"] || vars["_context"]) &&
#else
vars["_context"] && vars["_nick_place"] &&
#endif
(vars["_INTERNAL_control"] != "_silent" ||
v("entersilent") == "off")) {
P2(("irc/user: psyc2irc channel notice (%O)\n", mc))
// psyc2irc is called without source||context here so
// this will be server notices - it is yet to be
// determined if this is appropriate
output = psyc2irc(mc, 0) +" "+ // MYNICK+" "+
place2channel(vars["_nick_place"]) +" :" + output;
} else {
//PT(("irc/user: psyc2irc personal notice (%O) with %O\n", mc, vars))
output = psyc2irc(mc, source) +" "+MYNICK+" :" + output;
}
}
#ifdef NO_SMART_MSA
else {
// klammern und indent gemacht aus style-gruenden
for (i=strstr(output, "%"); i>=0; i=strstr(output, "%", i)) {
output[i] = 0x01; // support for msa's CTCP char
}
}
#endif
// i would really love to do it using the textdb, but it is unflexible.
// probably i'll change that somewhen.
if (mc == "_status_place_members_amount") {
// since irc ALWAYS needs to have a namreply, we
// simply give it one that only contains ourselves
w(mc[..<8], 0, ([ "_nick_place" : vars["_nick_place"],
"_members" : vars["_nick_me"] ]));
w(mc[..<8] + "_end", 0, vars);
}
emit(output);
}
// server:w() doesnt call this anyway, so we dont need last_prefix
//volatile private string last_prefix;
emit(string output) {
//PT(("common:emit %O %O\n", ME, output));
// misteries of virtual inheritance.. why doesnt this get called
// from gatebot? we need to get this working for 512-split!!
#ifdef _flag_log_sockets_IRC
# define EMIT(OUT) (log_file("RAW_IRC", "%d %O\t>> %s", time(), ME, OUT), ::emit(OUT))
# else
# define EMIT ::emit
#endif
#if __EFUN_DEFINED__(convert_charset)
string cs = qCharset();
if (cs && cs != SYSTEM_CHARSET) {
iconv(output, SYSTEM_CHARSET, cs);
P4(("output in %O = %O\n", cs, output))
}
#endif
if (output[<1] != '\n')
return EMIT(output);
if (strlen(output) < MAX_IRC_BYTES)
return EMIT(chomp(output) + "\r\n");
else {
string split_prefix, line;
int cut, t;
cut = strstr(output, " :");
P3(("IRC:emit splitting large line at %O\n%O\n", cut, output))
if (cut >= 0) {
split_prefix = output[..++cut];
output = output[++cut..<2];
if (output[0] == output[<1] && output[0] == 0x01) {
output = chop(output);
}
} else {
split_prefix = ""; //last_prefix;
output = chop(output);
}
// because of additional \\\r\n we have to subtract 3
cut = MAX_IRC_BYTES-3 - strlen(split_prefix);
if (cut < 9) {
// happens when 005 messages grows too big
P1(("%O encountered data w/out a decently placed ':' "
"to be able to do 512 splitting\n%O\n", ME, line))
return;
}
foreach(line : explode(output, "\n")) if (strlen(line)) {
// maybe we should just throw away everything beyond
while (strlen(line) > cut) {
#ifdef NOT_EXPERIMENTAL
// we shall look for last whitespace instead
t = rindex(line, ' ', cut);
if (t > 9) {
// msa's CTCP character
if (line[0] == 0x01) { // can only be true in
// the very first cycle.
// any realistic ideas
// how to do do this
// just once?
EMIT(split_prefix + line[..t]
+ line[0..0] + "\r\n");
line = line[t+1..];
} else {
EMIT(split_prefix + line[..t]+ "\r\n");
line = line[t+1..];
}
} else {
P1(("%O encountered data w/out ' ' to "
"allow for a decent IRC 512 split:"
"\n%O\n", ME, line))
// ignore this junk
return;
// we might aswell use the old
// backslash splitting code below here
}
#else
t = line[cut] == '\r' ? cut-1 : cut;
// msa's CTCP character
if (line[0] == 0x01) { // can only be true in
// the very first cycle.
// any realistic ideas
// how to do do this
// only once?
// inserting stargazer-style backslashes
EMIT(split_prefix + line[..t-1]
+ "\\" + line[0..0] + "\r\n");
line = line[cut..];
} else {
EMIT(split_prefix + line[..t]
+ "\\\r\n");
line = line[cut+1..];
}
#endif
}
EMIT(split_prefix + line +"\r\n");
}
}
return 1;
}
internalError() {
#ifdef RELAY
// yay. we could "fix" it here by
next_input_to(#'parse);
PT(("crazy experimental autofix in internalError()\n"))
#else
emit("ERROR :Congratulations! you triggered a runtime error in psyced."
" This server will no longer accept input until you reconnect.\n");
#endif
}

175
world/net/irc/decode.c Normal file
View file

@ -0,0 +1,175 @@
// $Id: decode.c,v 1.28 2008/01/21 10:25:12 lynx Exp $ // vim:syntax=lpc
//
// generic CTCP implementation, also includes color code filter.
// msa (Markku Savela, if i remember the spelling right) came up
// with the crazy idea of using binary code 1 as the escape code
// for CTCP, so we put that code into the variable msa.
//
#include <net.h>
volatile string msa, msare, cc, bc, uc;
// belongs into create() really..
decodeInit() {
msa = " "; msa[0] = 0x01;
msare = msa+".*"+msa;
cc = " "; cc[0] = 0x03; // same as msa
bc = " ", bc[0] = 0x02; // same as msa
uc = " ", uc[0] = 0x1F; // same as msa
}
// to be inherited
ctcp(type, text, target, req, srcnick, source) {
mapping vars;
P2(("%O %s %O -> %O: %O %O\n", ME,
req ? "REQ" : "REP",
srcnick, target, type, text))
vars = ([ "_nick" : srcnick,
"_value" : text || "",
"_type" : type ]);
sendmsg(target, (req? "_request": "_status") +"_legacy_CTCP",
"[_nick] "+ (req? "requests": "sends")+
" IRC legacy command '[_type]'.",
vars, source );
}
// to be inherited
version(text, target, req, srcnick, source) {
if (req && !text) {
P2(("%O VREQ(1=%O) %O -> %O: %O\n", ME, req,
srcnick, target, text))
sendmsg(target, "_request_version",
"[_nick] requests your version.",
([ "_nick" : srcnick ]), source );
#ifndef NO_IRC_AUTO_REQUEST_VERSION
} else if (target == query_server_unl()) {
if (text) vSet("agent", text);
#endif
} else {
PT(("%O VREP(0=%O) %O -> %O: %O\n", ME, req,
srcnick, target, text))
// generally unused as we don't send ctcp version to our clients
sendmsg(target, "_status_version_agent",
"Version: [_nick] is using \"[_version_agent]\" ([_version]).", ([
"_nick" : srcnick,
"_version" : SERVER_VERSION,
"_version_server": SERVER_DESCRIPTION,
"_version_agent" : text
]), source );
}
return;
}
// text, recipient string from irc, req: PRIVMSG/_request:1, NOTICE/_status:0
decode(text, rcpt, req) {
// filtering first.. 'cause some crazy people may be putting
// color codes inside of ctcp requests etc.
// filtering for mirc color codes
// these will never make it into a psyc standard ;)
// so they need to be removed
unless (strstr(text, cc) == -1) {
text = regreplace(text, cc + "[0-9][0-9],[0-9][0-9]", "", 1);
text = regreplace(text, cc + "[0-9][0-9],[0-9]", "", 1);
text = regreplace(text, cc + "[0-9],[0-9][0-9]", "", 1);
text = regreplace(text, cc + "[0-9],[0-9]", "", 1);
text = regreplace(text, cc + ",[0-9][0-9]", "", 1);
text = regreplace(text, cc + ",[0-9]", "", 1);
text = regreplace(text, cc + "[0-9][0-9]", "", 1);
text = regreplace(text, cc + "[0-9]", "", 1);
text = regreplace(text, cc, "", 1);
}
// filtering for irc bold code
unless (strstr(text, bc) == -1)
text = regreplace(text, bc, "", 1);
// filtering for irc underline code
unless (strstr(text, uc) == -1)
text = regreplace(text, uc, "", 1);
// it is more correct to check the whole text string for msa,
// but in practice no irc entity ever produces such a string
// with CTCP interspersed into the plain text, so were much
// more efficient to just look at the first character
if (text[0] == 0x01) {
string ctcpcode, ctcptext;
array(string) extract;
extract = regexplode(text, msare);
unless (sizeof(extract) >= 2) return; // unbalanced msa
ctcpcode = extract[1][1..<2];
sscanf(ctcpcode, "%s %s", ctcpcode, ctcptext);
ctcpcode = upper_case(ctcpcode);
P2(("CTCP %O w/ value %O found.\n", ctcpcode, ctcptext))
switch (ctcpcode) {
case "ACTION":
action(ctcptext, rcpt);
break;
case "VERSION":
unless (rcpt) break;
version(ctcptext, rcpt, req);
break;
case "PING":
if (rcpt)
if (req)
ping(rcpt, ctcptext);
// else ignore it for now as we cant tag it
break;
case "XCHAT":
// xchat sends an additional non-standard reply
// to the ctcp version request. we need to ignore
// this, instead of routing it to the server root.
break;
// this is an EXPERIMENTAL extension from fippo, but
// since this code can only be triggered by the
// appropriate irssi script, we can just leave it here
case "TYPING":
PT(("ctcp typing %O\n", ctcptext))
unless(rcpt) break;
switch(ctcptext) {
case "INACTIVE":
case "PAUSE":
case "COMPOSE":
case "ACTIVE":
case "GONE":
tell(rcpt, "", 0, 0, "_notice_typing_" + lower_case(ctcptext));
break;
default:
ctcp(ctcpcode, ctcptext, rcpt, req);
}
break;
default:
unless (rcpt) break;
ctcp(ctcpcode, ctcptext, rcpt, req);
}
return 0;
}
return text;
}
// ein klein wenig ratlosigkeit
#if 1
irctext(template, vars, data, source) {
int i;
string output = psyctext(template, vars, data, source);
for (i=strstr(output, "%"); i>=0; i=strstr(output, "%", i)) {
output[i] = 0x01; // support for msa's CTCP char
}
return output;
}
#else
ircp(output) {
int i;
for (i=strstr(output, "%"); i>=0; i=strstr(output, "%", i)) {
output[i] = 0x01; // support for msa's CTCP char
}
return emit(output);
}
#endif

218
world/net/irc/error.h Normal file
View file

@ -0,0 +1,218 @@
// $Id: error.h,v 1.3 2005/03/14 10:23:27 lynx Exp $ // vim:syntax=lpc
//
// automatically extracted from RFC 1459, chapter 6. thanks sique.
#define ERR_NOSUCHNICK "401" // Used to indicate the nickname parameter
// supplied to a command is currently unused.
#define ERR_NOSUCHSERVER "402" // Used to indicate the server name given
// currently doesn't exist.
#define ERR_NOSUCHCHANNEL "403" // Used to indicate the given channel name
// is invalid.
#define ERR_CANNOTSENDTOCHAN "404" // Sent to a user who is either (a) not on
// a channel which is mode +n or (b) not a
// chanop (or mode +v) on a channel which
// has mode +m set and is trying to send
// a PRIVMSG message to that channel.
#define ERR_TOOMANYCHANNELS "405" // Sent to a user when they have joined
// the maximum number of allowed channels
// and they try to join another channel.
#define ERR_WASNOSUCHNICK "406" // Returned by WHOWAS to indicate there
// is no history information for that
// nickname.
#define ERR_TOOMANYTARGETS "407" // Returned to a client which is attempting
// to send a PRIVMSG/NOTICE using the
// user@host destination format and for a
// user@host which has several occurrences.
#define ERR_NOORIGIN "409" // PING or PONG message missing the
// originator parameter which is required
// since these commands must work
// without valid prefixes.
#define ERR_NORECIPIENT "411" // 412 - 414 are returned by PRIVMSG to
#define ERR_NOTEXTTOSEND "412" // indicate that the message wasn't
#define ERR_NOTOPLEVEL "413" // delivered for some reason.
#define ERR_WILDTOPLEVEL "414" // ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL
// are errors that are returned when an
// invalid use of "PRIVMSG $<server>" or
// "PRIVMSG #<host>" is attempted.
#define ERR_UNKNOWNCOMMAND "421" // Returned to a registered client to
// indicate that the command sent is
// unknown by the server.
#define ERR_NOMOTD "422" // Server's MOTD file could not be opened
// by the server.
#define ERR_NOADMININFO "423" // Returned by a server in response to an
// ADMIN message when there is an error in
// finding the appropriate information.
#define ERR_FILEERROR "424" // ???
#define ERR_NONICKNAMEGIVEN "431" // Returned when a nickname parameter
// expected for a command and isn't found.
#define ERR_ERRONEUSNICKNAME "432" // Returned after receiving a NICK message
// which contains characters which do not
// fall in the defined set. See section
// x.x.x for details on valid nicknames.
#define ERR_NICKNAMEINUSE "433" // Returned when a NICK message is
// processed that results in an attempt to
// change to a currently existing nickname.
#define ERR_NICKCOLLISION "436" // Returned by a server to a client when it
// detects a nickname collision (registered
// of a NICK that already exists by another
// server).
#define ERR_USERNOTINCHANNEL "441" // Returned by the server to indicate that
// the target user of the command is not on
// the given channel.
#define ERR_NOTONCHANNEL "442" // Returned by the server whenever a client
// tries to perform a channel effecting
// command for which the client isn't a
// member.
#define ERR_USERONCHANNEL "443" // Returned when a client tries to invite a
// user to a channel they are already on.
#define ERR_NOLOGIN "444" // Returned by the summon after a SUMMON
// command for a user was unable to be
// performed since they were not logged in.
#define ERR_SUMMONDISABLED "445" // Returned as a response to the SUMMON
// command. Must be returned by any server
// which does not implement it.
#define ERR_USERSDISABLED "446" // Returned as a response to the USERS
// command. Must be returned by any server
// which does not implement it.
#define ERR_NOTREGISTERED "451" // Returned by the server to indicate that
// the client must be registered before the
// server will allow it to be parsed in
// detail.
#define ERR_NEEDMOREPARAMS "461" // Returned by the server by numerous
// commands to indicate to the client that
// it didn't supply enough parameters.
#define ERR_ALREADYREGISTRED "462" // Returned by the server to any link which
// tries to change part of the registered
// details (such as password or user
// details from second USER message).
#define ERR_NOPERFORHOST "463" // Returned to a client which attempts to
// register with a server which does not
// been setup to allow connections from the
// host the attempted connection is tried.
#define ERR_PASSWDMISMATCH "464" // Returned to indicate a failed attempt at
// registering a connection for which a
// password was required and was either not
// given or incorrect.
#define ERR_YOUREBANNEDCREEP "465" // Returned after an attempt to connect and
// register yourself with a server which
// has been setup to explicitly deny
// connections to you.
#define ERR_KEYSET "467" // Any command requiring operator
// privileges to operate must return this
// error to indicate the attempt was
// unsuccessful.
#define ERR_CHANNELISFULL "471" //
#define ERR_UNKNOWNMODE "472" //
#define ERR_INVITEONLYCHAN "473" //
#define ERR_BANNEDFROMCHAN "474" //
#define ERR_BADCHANNELKEY "475" //
#define ERR_NOPRIVILEGES "481" //
#define ERR_CHANOPRIVSNEEDED "482" // Any command requiring 'chanop'
// privileges (such as MODE messages) must
// return this error if the client making
// the attempt is not a chanop on the
// specified channel.
#define ERR_CANTKILLSERVER "483" // Any attempts to use the KILL command on
// a server are to be refused and this
// error returned directly to the client.
#define ERR_NOOPERHOST "491" // If a client sends an OPER message and
// the server has not been configured to
// allow connections from the client's host
// as an operator, this error must be
// returned.
#define ERR_UMODEUNKNOWNFLAG "501" // Returned by the server to indicate that
// a MODE message was sent with a nickname
// parameter and that the a mode flag sent
// was not recognized.
#define ERR_USERSDONTMATCH "502" // Error sent to any user trying to view or
// change the user mode for a user other
// than themselves.
#if 0
#define ERROR_LIST ([\
ERR_NOSUCHNICK: "%s :No such nick/channel",\
ERR_NOSUCHSERVER: "%s :No such server",\
ERR_NOSUCHCHANNEL: "%s :No such channel",\
ERR_CANNOTSENDTOCHAN: "%s :Cannot send to channel",\
ERR_TOOMANYCHANNELS: "%s :You have joined too many channels",\
ERR_WASNOSUCHNICK: "%s :There was no such nickname",\
ERR_TOOMANYTARGETS: "%s :Duplicate recipients. No message \
delivered",\
ERR_NOORIGIN: ":No origin specified",\
ERR_NORECIPIENT: ":No recipient given (<command>)",\
ERR_NOTEXTTOSEND: ":No text to send",\
ERR_NOTOPLEVEL: "%s :No toplevel domain specified",\
ERR_WILDTOPLEVEL: "%s :Wildcard in toplevel domain",\
ERR_UNKNOWNCOMMAND: "%s :Unknown command",\
ERR_NOMOTD: ":MOTD File is missing",\
ERR_NOADMININFO: "%s :No administrative info available",\
ERR_FILEERROR: ":File error doing %s on %s",\
ERR_NONICKNAMEGIVEN: ":No nickname given",\
ERR_ERRONEUSNICKNAME: "%s :Erroneus nickname",\
ERR_NICKNAMEINUSE: "%s :Nickname is already in use",\
ERR_NICKCOLLISION: "%s :Nickname collision KILL",\
ERR_USERNOTINCHANNEL: "%s %s :They aren't on that channel",\
ERR_NOTONCHANNEL: "%s :You're not on that channel",\
ERR_USERONCHANNEL: "%s %s :is already on channel",\
ERR_SUMMONDISABLED: ":SUMMON has been disabled",\
ERR_USERSDISABLED: ":USERS has been disabled",\
ERR_NOTREGISTERED: ":You have not registered",\
ERR_NEEDMOREPARAMS: "%s :Not enough parameters",\
ERR_ALREADYREGISTRED: ":You may not reregister",\
ERR_YOUREBANNEDCREEP: ":You are banned from this server",\
ERR_KEYSET: "%s :Channel key already set",\
ERR_CHANNELISFULL: "%s :Cannot join channel (+l, Limit exceeded)",\
ERR_UNKNOWNMODE: "%s :is unknown mode char to me",\
ERR_INVITEONLYCHAN: "%s :Cannot join channel (+i, Invite channel)",\
ERR_BANNEDFROMCHAN: "%s :Cannot join channel (+b, You are banned from \
channel)",\
ERR_BADCHANNELKEY: "<channel> :Cannot join channel (+k, wrong channel \
key)",\
ERR_NOPRIVILEGES: ":Permission Denied- You're not an IRC operator",\
ERR_CHANOPRIVSNEEDED: "%s :You're not channel operator",\
ERR_CANTKILLSERVER: ":You cant kill a server!",\
ERR_UMODEUNKNOWNFLAG: ":Unknown MODE flag",\
ERR_USERSDONTMATCH: ":Cant change mode for other users",\
])
#endif

912
world/net/irc/gatebot.c Normal file
View file

@ -0,0 +1,912 @@
// $Id: gatebot.c,v 1.146 2008/03/11 13:42:26 lynx Exp $ // vim:syntax=lpc
//
// the PSYC-IRC gateway robot.
// based on the ircbot.c in Nemesis that i wrote in 1992. -lynX
// documentation: http://www.psyc.eu/ircgate
// it can still use plenty of improvements and new features, just go ahead!
// gatebot hands out some room status to the channel:
// -|symlynX/#23c3- PSYC Identification of freeNode: psyc://psyced.org/@freenode
// -|symlynX/#23c3- You enter freeNode.
// "you" is obviously wrong here. also why |symlynX rather than PSYCgate?
// TODO? fippo doesn't like #ifdefs, so he wrote the next generation gateway.c
// that allows for entering channels on the ircnet in a way not compatible
// to this file. How can we merge this back? Can we handle things better?
//
// #ifdef SERVER is a bit ugly, yet having several files would result
// in almost the same code in several files, just slightly different. We can
// try to simplify some ifdefs via textdb. Then again, I don't think #ifdef
// is really that bad. The biggest problem of ifdef is not the preprocessing
// as such, it's just how ugly preprocessor statements look. Maybe a macro
// would help? As in GATEWAY( ...code... ) vs GATEBOT( ...code... ). Hm!
//
// Several files would look like this: Put the majority of gatebot.c in a
// gateshare.i or even gateshare.c, then have gatebot.c and gateway.c
// include or inherit it and adapt. The stupid thing is that functions like
// disc() or reboot() contain almost the same code for both variants and
// should better stay the way they are - together. And once gateway.c has
// learned how to enter channels, wouldn't you want gatebot.c to be able
// to do that as well? So we have to code it in a generic way, anyway.
// Sure, it's harder to do abstract coding, but the benefit can be double.
// Not only you solve two things at once, you also only have one place to
// deal with when something needs to be fixed. Having two implementations
// of the same thing wouldn't be what we want, anyway.
#include <net.h>
#include <status.h>
#include <services.h>
#include <text.h>
#include <url.h>
#include "gatebot.h"
#include "error.h" // gets numeric codes
#include "reply.h" // gets numeric codes
// ctcp support etc.. yes even dcc works across the gateway if the psyc
// user runs an irc client
inherit IRC_PATH "decode";
// message queueing and automatic reconnect mgmt
inherit NET_PATH "circuit";
#ifndef SERVER_URL
# define SERVER_URL query_server_unl()
#endif
#ifdef RELAY
# define IRCER_UNIFORM(NICK) (SERVER_URL +"~"+ NICK)
#else
// will upgrade to irc: syntax..
# define IRCER_UNIFORM(NICK) (MYLOWERNICK +":"+ NICK)
#endif
// dieses objekt ist ein blueprint für einen raum -
// d.h. man kann ircgateways betreten und auf diese weise
// nicht zuordnungsfähige protokollreplies von irc abholen
// (irc bietet kein request/response-tagging, und andere
// lösungsansätze sind gepfriemel)
#ifdef IRCGATE_HISTORY
inherit NET_PATH "place/storic";
#else
// we normally don't want to irritate our fellow ircers by
// keeping logs of their conversations. it's atypical to
// irc traditions. unfortunately we can take this decision
// only systemwide as of now.
inherit NET_PATH "place/public";
#endif
#ifndef SERVER
# undef IRCGATE_REGISTERED_ONLY
// the gateway command level also used by other gateways like ICQ
inherit NET_PATH "gateway/generic";
#else
// allow federated communications for registered nicks only.
// this could've been implemented in the ircd aswell...
# define psycer rcpt
# ifdef ADVERTISE_PSYCERS
volatile mapping gated;
# endif
# ifdef IRCGATE_REGISTERED_ONLY
volatile mapping regged;
# endif
volatile string joe, joe_nick;
volatile mixed joe_unl;
#endif
volatile int notice;
volatile string names, namesfrom, namesto;
qChatChannel () { }
#ifdef SERVER
ulogin(ni) {
#ifndef IRCGATE_NO_LOGIN
object u = summon_person(ni, IRC_PATH "ghost");
if (u) u -> logon();
#endif
#ifdef IRCGATE_REGISTERED_ONLY
regged[lower_case(ni)]++;
#endif
}
ulogout(ni) {
#ifndef IRCGATE_NO_LOGIN
mixed u = find_person(ni);
if (u) return u->quit();
#endif
#ifdef IRCGATE_REGISTERED_ONLY
ni = lower_case(ni);
if (regged[ni]) {
regged[ni] = 0;
}
// PT(("regged: %O\n", regged))
#endif
}
emit(all, source) {
#ifdef ADVERTISE_PSYCERS
if (source) advertise(source);
#endif
#ifdef _flag_log_sockets_IRC
log_file("RAW_IRC", "%d %O\t-> %s", time(), ME, all);
#endif
return ::emit(all);
}
qName() { return joe_nick; }
# if 0
// circumventing library:psyc2irc with its special cases
psyc2irc(mc, source) {
PT(("psyc2irc wird einfach nicht aufgerufen. arggh!!\n"))
return ":" IRCGATE_NICK " NOTICE";
}
# endif
# ifdef ADVERTISE_PSYCERS
advertise(source) {
unless (gated[source]) {
mixed *u;
string login;
// in this case 'gated' doesn't strictly mean this source
// is already advertised but rather this source has already
// been considered for advertising and either been advertised
// or not..
gated[source] = 1;
if (!stringp(source) || !strlen(source)) return;
unless (u = parse_uniform(source)) return;
if (login = u[UUser]) {
#if 1
// for some mysterious reason sporadically we get
// UNRs here. maybe we should raise_error to figure
// out why that happens.
// since gmail likes to change its UNRs quite often,
// this results in 'gated' getting slowly polluted.
// we could either fight this by storing timestamps
// in gated[] and weeding out old entries occasionally
// (how do we find out if an entry has actually been
// advertised and thus needs to be QUITted? parse again?).
// the other option is to have psyced periodically
// reconnect the ircnet. sufficient intermediate solution
// really, since the resync between psyced and a locally
// running ircd is merely cpu exercise, not traffic.
// this whole problem should no longer occur, since
// fippo took a measure in person.c to avoid UNRs
// arriving here. i wonder why person.c gets them
// in the first place. shouldn't entity.c deal with
// them? hmmmm
if (u[UResource]) {
monitor_report("_failure_invalid_identity",
sprintf("%O advertising UNR %O from %O",
ME, source, previous_object()));
source = u[UScheme] +":"+ u[UUserAtHost];
// of course we need to check this variant, too. thx fippo.
if (gated[source]) return 1;
gated[source] = 1;
}
#endif
} else {
// maybe this kind of logic should be in parse_uniform?
// or is it too rarely needed thus inefficient?
login = u[UResource];
if (stringp(login) && strlen(login) > 1)
login = login[1 ..];
}
unless (login && strlen(login)) return;
// introduce the federation user into the ircnet
// database. not strictly necessary for plain
// messaging but surely as soon as the person wants
// to join an ircnet channel.
P0(("ADVERTISING %O as %O of %O\n",
source, login, u[UHost]))
// hmm PTLINK syntax.. we may have to add some ifdefs here for other flavors
// or maybe we do just like with login() and throw this into the ircgate.c
/* m_nick
** parv[0] = sender prefix
** parv[1] = nickname
** parv[2] = optional hopcount when new user; TS when nick change
** parv[3] = optional TS
** parv[4] = optional umode
** parv[5] = optional username
** parv[6] = optional hostname
** parv[7] = optional spoofed hostname
** parv[8] = optional server
** parv[9] = optional info
*/
emit("NICK "+ source +" 1 4404 +i " + login + " " SERVER_HOST
" " + u[UHost] + " " IRCGATE_NICK " :I'm psyced!\n");
}
// we could update a timestamp in gated[] so every 15m we
// can clean out old names.
}
//static send(source, text) {
render(mc, data, vars, source) {
//# include <url.h>
// mixed *u = parse_uniform(psyc_name(source));
// unless (mappingp(u) && u[UHost]) {
// P1(("failed\n"))
// return;
// }
// string ircsrc = (u[UScheme] || "xmpp") +";"+
// (u[UUser] || u[UResource][1..]) +"|"+
// replace(u[UHost], ".", "_");
if (source) {
advertise(source);
// text = ":"+ source +" "+ text;
}
// PT(("gateOut: "+ text))
// emit(text);
return ::render(mc, data, vars, source);
}
# endif
#else
# define send(source, text) emit(text)
#endif
showStatus(verbosity, al, person, mc, data, vars) {
// this is for my local users only
// TODO: this info doesn't get updated on joins and leaves
// re-request NAMES every time? damn.
if (names && namesfrom) sendmsg(person, "_status_place_members_IRC",
"irc://[_server_IRC]/[_nick_place_IRC] contains: [_members_IRC]",
([ "_members_IRC": names,
//"_source_IRC": "irc://"+namesfrom+"/"+namesto
"_server_IRC": namesfrom,
"_nick_place_IRC": namesto ]));
return ::showStatus(verbosity, al, person, mc, data, vars);
}
link(ho, po) {
register_scheme(MYLOWERNICK);
joe = "nobody";
// we cannot use "irc" here, because we don't want the textdb
// to do the irc protocol formatting for us. we need a plain-text
// formatting of the messages that we can put into NOTICEs etc
// "ircgate" simply gives us the chance to change a few message
// formats specifically for the gateway, textdb inheritance takes
// care of the rest. theoretically we could enable multiple
// languages here, but uh.. do you care?
#ifdef SERVER
sTextPath(0, 0, "irc");
#else
sTextPath(0, 0, "ircgate");
#endif
decodeInit();
return ::circuit(ho, po || IRC_SERVICE);
}
ircMsg(from, cmd, args, text, all) {
string *a, t, rcpt, deb;
//PT(("ircMsg(%O,%O,%O,%O)\n", from,cmd,args,text))
notice = 0;
next_input_to(#'parse);
a = explode(cmd+" "+args, " "); // TODO! redefine a properly
rcpt = sizeof(a)>1 && a[1];
// here we presume PSYC.EU is uppercase!
if (strlen(rcpt) == sizeof(IRCGATE_NICK)
&& upper_case(rcpt) == IRCGATE_NICK) rcpt = 0;
#define DEB if (!deb) deb = MYNICK+" "+(from||"")+" "+\
(t||"")+" '"+(text||"")+"'\n"
// DT( if (cmd != "ping") { DEB; D(deb); } )
switch(cmd) {
case "ping":
//emit(SERVER_SOURCE "PONG " SERVER_HOST " :"+ MYNICK +"\n");
// emit("PONG " SERVER_HOST " :"+ MYLOWERNICK +"\n");
// emit("PONG " SERVER_HOST " :"+ rcpt +"\n");
emit("PONG " IRCGATE_NICK " :"+ text +"\n");
return 1;
#ifndef RELAY
case "notice":
#ifndef RELAY_SMART_IRCSERVS
// would be better if servs weren't talking to us!!
if (strlen(from) > 7 && from[4 .. 7] == "Serv") {
// check for 'Nick' and 'Chan' ?
P2(("... ignoring crap from "+ from +"\n"))
return 1;
}
#endif
// strange behaviour of some ircds.. sending notices
// to a virtual user called 'auth' before login
// correct behaviour is to send "NOTICE *"
if (rcpt == "*"
#ifdef SERVER
|| rcpt == IRCGATE_NICK
#endif
|| rcpt == "AUTH") return 1;
// notice from my or any other server
if (from && index(from, '.') > 0) {
joe = from;
from = 0;
}
notice = 1; // fall thru
case "privmsg":
if (joe_nick = from) {
joe = joe_nick;
if (sscanf(joe, "%s!%s@%s", joe_nick)) {
// if (stupidNetwork())
joe = joe_nick;
}
joe_unl = IRCER_UNIFORM(joe);
joe = lower_case(joe);
}
#ifdef SERVER
# ifdef IRCGATE_REGISTERED_ONLY
unless (regged[joe]) {
// emit(SERVER_SOURCE + ERR_SUMMONDISABLED +" "+
// joe_nick +" :You're not registered yet.\n";
render("_error_necessary_registration",
"Sorry, you cannot use this without prior registration.", ([
"_source_hack": IRCGATE_NICK,
"_nick_me" : joe_nick,
]));
return 1;
}
# endif
# ifndef IRCGATE_FULL_UNIFORM_SUPPORT
// also accept uniform@PSYC syntax.. necessary when the
// ircd has not been patched to always forward uniforms
// to the psyced. newer patches however do that, so we
// should not need this.
if (upper_case(rcpt[<sizeof(IRCGATE_NICK) ..]) == IRCGATE_NICK)
rcpt = rcpt[0 .. <sizeof(IRCGATE_NICK)+2];
# endif
PT(("the rcpt is %O for %O from %O\n", rcpt, text, joe_unl))
if (!rcpt || channel2place(rcpt)) pubmsg(text);
else {
unless (text = decode(text, rcpt, !notice)) break;
sendmsg(rcpt, notice? "_message_private_annotate":
"_message_private", text,
([ "_nick" : joe_nick ]), joe_unl);
}
#else
unless (rcpt && (rcpt = channel2place(rcpt))) rcpt = 0;
unless (text = decode(text, rcpt, !notice)) break;
if (rcpt) {
if (from) pubmsg(text);
// weird hack, i know
else castmsg(ME, "_notice_IRC", "[_server]: [_text]",
([ "_text": text, "_server": joe ]) );
}
else unless (notice) input(text);
#endif
break;
#endif
case ERR_NOSUCHNICK:
#ifdef SERVER
if (rcpt && sizeof(a)>2)
sendmsg(rcpt, "_error_unknown_name_user",
"[_nick_target] isn't available.",
([ "_nick_target": IRCER_UNIFORM(a[2]) ]) );
#else
if (psycer) sendmsg(psycer, "_error_unknown_name_user_IRC",
from+": "+text+"\n");
#endif
break;
case RPL_AWAY:
if (psycer) sendmsg(psycer, "_status_person_absent_IRC",
from+" is away: "+text+"\n");
break;
case RPL_WHOREPLY:
// parsewho(a, text);
break;
case RPL_LUSERCLIENT:
monitor_report("_notice_circuit_established_IRC",
from+": "+text);
break;
case RPL_NAMREPLY:
names = text; namesfrom = from; namesto = a[3];
#ifdef CHAT_CHANNEL
castmsg(ME, "_notice_place_members_IRC",
"On [_nick_place_IRC]: [_members_IRC]",
([ "_members_IRC": names,
"_source_IRC": namesfrom,
"_nick_place_IRC": namesto ]));
#endif
#ifndef SERVER
monitor_report("_notice_place_members_IRC",
"irc://"+namesfrom+"/"+namesto+" contains: "+names);
break;
case "join":
unless (rcpt) rcpt = text; // historic syntax bug in JOIN
if (rcpt && (rcpt = channel2place(rcpt))) {
if (stricmp(rcpt, qChatChannel())) {
P0(("%O got autojoined into %O\n", ME, rcpt))
emit("PART #"+rcpt+
" :looks like I was autojoined here\n");
break;
}
} else rcpt = 0;
// fall thru
case "quit": // IRC QUIT is broken really
case "part":
sscanf(from, "%s!%s@%s", t);
castmsg(IRCER_UNIFORM(from), "_notice_place_IRC_"+cmd,
"[_nick] "+ cmd +"s [_channel].",
([ "_nick" : t, "_channel" : (rcpt||text) ]));
break;
case "001":
#ifndef SERVER
emit("JOIN #"+qChatChannel()+" "+CHAT_CHANNEL_MODE+"\n");
#endif
#ifdef EXTRA_LOGON
emit(EXTRA_LOGON+"\n");
#endif
// calling showStatus on myself for my fellow ircers
::showStatus(VERBOSITY_IRCGATE_LOGON, 0, ME);
break;
case "002":
case "003":
case "004":
case "005":
case RPL_LUSEROP:
case RPL_LUSERUNKNOWN:
case RPL_LUSERCHANNELS:
case RPL_LUSERME:
case "250":
// Highest connection count: 775 (774 clients) (19656 connections received)
case "265":
case "266":
case RPL_MOTDSTART:
case RPL_MOTD:
case RPL_ENDOFMOTD:
//case ERR_NOSUCHCHANNEL: // UMODE error messages from old servers
case RPL_ENDOFNAMES:
case RPL_ENDOFWHO:
break; // ignore'm all
default:
#ifdef RELAY
if (strlen(from) && index(from, '.') == -1) {
mixed u = find_person(from);
PT(("cmd:relay %O to %O!\n", cmd, u))
if (u) return u->ircMsg(from, cmd, args, text, all);
}
#else
DEB;
castmsg(ME, "_notice_unexpected_message_IRC", chomp(deb), ([]));
// D1(D(deb);)
#endif
// fall thru
case ERR_NOSUCHSERVER:
case "mode":
DEB;
log_file("IRCEVENT", deb);
#else
case "part": // careful about PART in particular:
// normal irc/user behaviour is to echo this
// which in S2S makes no sense!
break;
case "sjoin":
# if 0 // taken from gateway.c, works quite well, but the place is too noisy
{
// sjoin timestamp channel modelist :list-of-nicks
rcpt = find_place(channel2place(a[2]));
string *members;
members = map(explode(trim(text), " "),
// watch out, this should to be adapted to the
// channel privileges (like chanadmin (.),
// chanpop (@), halfop (%) and voice(+)).
// ifdef as needed
(:
return (
$1[0] == '@' ||
$1[0] == '+' ||
$1[0] == '.' ||
$1[0] == '%')
? $1[1..] : $1;
:));
foreach (string member : members) {
sendmsg(rcpt, "_request_enter", 0,
([ "_nick" : member,
"_INTERNAL_origin" : ME ]), summon_person(member));
}
}
# endif
break;
case "topic": // let's postpone these complications a bit
# if 0 // likewise from gateway.c
{
// source TOPIC channel topic-user timestamp :text
// TODO: we need to catch the "answer" to that
rcpt = find_place(channel2place(a[1]));
sendmsg(rcpt, "_request_set_topic", 0,
([
"_topic" : text,
"_nick" : a[2] ]),
summon_person(a[2]));
}
# endif
#endif
break;
#ifdef SERVER
case "gline":
// block'ing may be appropriate...
// relaying to a remote place is not,
// ignore it for now
break;
case "nnick": // ptlink6 style
case "nick":
PT(("NICK START\n"))
if (trail("Serv", rcpt)) return 1;
#ifndef IRCGATE_REGISTERED_ONLY
ulogin(rcpt);
#endif
// combined nick/user announcement
if (sizeof(a)>4) {
#ifdef IRCGATE_REGISTERED_ONLY
if (index(a[4], 'r') >= 0)
ulogin(rcpt);
#endif
return 1;
}
// otherwise nickchange, fall thru
case "quit":
if (from) ulogout(from);
PT(("QUIT NICK\n"))
return 1;
#ifdef IRCGATE_REGISTERED_ONLY
case "mode":
if (strlen(text) && index(text, 'r') >= 0) {
// regged[lower_case(rcpt)] = text[0] == '-' ? 0 : 1;
if (text[0] == '-') ulogout(rcpt);
else ulogin(rcpt);
}
#endif
return 1;
#endif
}
return 1;
}
action(text, rcpt) {
#ifdef SERVER
if (rcpt) sendmsg(rcpt, "_message_private", 0,
([ "_nick" : joe_nick, "_action": text ]), joe_unl);
#else
if (!rcpt && talk[joe])
sendmsg(talk[joe], "_message_private", 0,
([ "_nick" : joe_nick, "_action": text ]), joe_unl);
#endif
else castmsg(joe_unl, "_message_public", 0,
([ "_nick" : joe_nick, "_action": text ]));
}
// incomplete TODO
version(text, rcpt, req) {
#ifdef SERVER
if (rcpt)
::version(text, rcpt, req, joe_nick, joe_unl);
#else
if (talk[joe])
::version(text, talk[joe], req, joe_nick, joe_unl);
#endif
//else
// send *my* version info
}
ctcp(code, text, rcpt, req) {
#ifdef SERVER
if (rcpt)
::ctcp(code, text, rcpt, req, joe_nick, joe_unl);
#else
if (talk[joe])
::ctcp(code, text, talk[joe], req, joe_nick, joe_unl);
#endif
}
static pubmsg(text, ext) {
castmsg(joe_unl, ext ? "_message_public_external" : "_message_public",
text, ([ "_nick" : joe_nick ]));
}
unknownmsg(text) {
PT(("unknown: "+text+"\n"))
if (abbrev("*** ", text)) {
text = text[4..];
notice = 1;
}
pubmsg(text, 1);
if (notice) return;
// P1(("%s tells %s: %s [%O]\n", joe, MYNICK, text, talk))
// reply("Your message has been relayed. Try HELP for help.");
#ifndef SERVER
return ::unknownmsg(text);
#endif
}
#ifndef SERVER
static reply(text) {
if (text && text != "") emit("NOTICE "+joe+" :"+text+"\n");
}
static stat() {
// calling showStatus on myself for my friend joe
::showStatus(VERBOSITY_IRCGATE_USER, 0, ME);
return ::stat();
}
#endif
reboot(reason, restart, pass) {
#ifdef SERVER
emit("SQUIT :Service "+ (restart ? "restart" : "shutdown")
+": "+ reason +"\n");
#else
emit("QUIT :Service "+ (restart ? "restart" : "shutdown")
+": "+ reason +"\n");
#endif
return ::reboot(reason, restart, pass);
}
static disc() {
log_file("IRC_BOSS", "%O disconnects %O\n", previous_object(), ME);
#ifdef SERVER
emit("SQUIT :Service temporarily disabled\n");
#else
emit("QUIT :Service temporarily disabled\n");
#endif
return 1;
}
logon(failure) {
int rc = ::logon(failure);
unless (rc) return 0;
#ifdef SERVER
# ifdef ADVERTISE_PSYCERS
gated = ([]);
# endif
# ifdef IRCGATE_REGISTERED_ONLY
regged = ([]);
# endif
// leave login procedure to place.gen
#else
emit("NICK "+ IRCGATE_NICK +"\nUSER "+ IRCGATE_USERID
+" . . :"+ IRCGATE_NAME +"\n"
#ifdef IRCGATE_HIDE
+"MODE "+ IRCGATE_NICK +" +i\n"
#endif
);
#endif
next_input_to(#'parse);
call_out(#'runQ, 3); // deliver the queue of messages in circuit.c
return rc;
}
//psycName() { return "@"+MYLOWERNICK; }
#if 0
static parsewho(a, text) {
// string channel, uid, host, server, nik, stat, hops, ircname;
// sscanf(s, "%s %s %s %s %s %s :%s %s", channel, uid, host, server, nik, stat, hops, ircname);
sscanf(text, "%s %s", hops, ircname);
host = a[2];
nik = a[4];
if (!nik || host == "Host") return;
if (wflag == "l" && ircname)
sendmsg(wuser, "_who", nik+" ("+uid+"@"+host+") is \""+ircname+"\"\n");
else
sendmsg(wuser, "_who", ladjust(nik,10)+"("+uid+"@"+host+")\n");
}
// old stuff from the MUD gateway
ircwho(mask) {
if (!interactive(ME)) return;
log_file("IRCWHO", mask ? mask : "(null)");
wflag = 0;
if (mask && !sscanf(mask, "-%s %s", wflag, mask)
&& sscanf(mask, "-%s", wflag))
mask = 0;
if (!mask || strlen(mask) < 3) mask = DEF_WHOMASK;
emit("WHO "+mask+"\n");
wuser = this_player();
return 1;
}
// Quote function for gods, use with marker
quote(text) {
if (!userp(this_player())
|| this_player()->query_level() < LEVEL_GOD) return;
sender = this_player();
emit(text+"\n");
return 1;
}
static plist() {
array(string) list=explode(read_file(TOP_PLAYERS_FILE),"\n");
for (int i=0; i<sizeof(list); i++) {
reply(list[i]);
}
return;
}
#endif
disconnect(remainder) {
// we typically just want to give it one more try
reconnect();
// the reconnect in circuit.c will do certain amount of retries only
return 0; // unexpected
}
#ifdef CHAT_CHANNEL
// place specific methods..
tellChannel(source, mc, vars, data) {
#ifndef SERVER
data = irctext( T(mc, ""), vars, data, source );
PT(("tellChannel(%O) %O\n", mc, data))
send(source, "NOTICE #"+qChatChannel()+" :"+ data +"\n");
#endif
}
msg(source, mc, data, mapping vars, showingLog, target) {
string t, ni, mca;
if (showingLog) return 1;
PT(("GATEMSG(%O,%O,%O,%O,%O,%O)\n", source,mc,data,vars,
showingLog,target))
unless(interactive()) {
connect();
// special requirement for enqueue
unless (vars["_source"])
vars["_source"] = UNIFORM(source);
return enqueue(source, mc, data, vars, showingLog, target);
}
if (abbrev("_status_place",mc) || abbrev("_message_announcement",mc)) {
if (source && source != ME) {
monitor_report("_error_rejected_message_IRC",
S("Caught a %O from %O (%O)", mc, source, data));
return;
}
if (joe_unl && target == joe_unl)
reply(irctext( T(mc, ""), vars, data, source ));
else tellChannel(source, mc, vars, data);
//
// i don't know why it doesn't show member listings to
// psyced-ircd's - must be something in net/irc/user.c -
// but we're not using this on psyceds anyway..
return 1;
}
if (!data && stringp(vars["_action"]) && abbrev("_message", mc))
mca = mc +"_action";
else
mca = mc;
// und DCC und freundschaft und /req ... benötigt alles
// eine generischere oder eben eigene irc-textdb
// hmm... echo geht immernoch nicht.. -|klotz- You tell 0: TODO
if (abbrev("_message_echo", mc)) return;
P4(("gatebot %O,%O,%O target: %O\n", source,mc,data, target))
#ifndef RELAY
if (target && stringp(target) && abbrev(MYLOWERNICK, target))
{
#if 0
//unless (stupidNetwork())
unless (sscanf(target[1+strlen(MYLOWERNICK)..], "%s!%~s@%~s", ni) == 3)
ni = target[1+strlen(MYLOWERNICK)..];
#else
//if (stupidNetwork())
ni = target[1+strlen(MYLOWERNICK)..];
#endif
#else
ni = target;
#endif
# ifdef IRCGATE_REGISTERED_ONLY
unless (regged[lower_case(ni)]) {
sendmsg(source, "_error_unknown_name_user",
"No [_nick_target] is available for talking.",
([ "_nick_target" : ni ]));
return 1;
}
# endif
#ifndef SERVER
psycer = source;
if (stringp(source)) vars["_nick"] = source; // remote psycer
// t = irctext( T(mca, 0), vars, data, source );
#else
t = irctext( T(mca, data), vars, data, source );
#endif
if (t && strlen(t)>5)
{
int pm = 0;
if (abbrev("_message", mc)) {
unless (trail("_annotate", mc)) pm = 1;
// irc doesn't echo, so we "fake" it here
sendmsg(source, "_message_echo"+ mc[8..], data, vars);
} else if (abbrev("_request", mc))
pm = 1;
#ifndef SERVER
send(source, (pm ? "PRIVMSG ": "NOTICE ") + ni
+" :"+ t +"\n");
if (pm && !talk[ni]) {
joe_unl = target;
joe_nick = ni;
joe = lower_case(joe_nick);
talkto(source);
}
#else
// TODO: remove 'pm' logic?
vars["_source_hack"] = source;
vars["_nick_me"] = ni;
//send(source, psyctext(t, vars, data, source));
render(mca, data, vars, source);
#endif
}
#ifndef RELAY
else sendmsg(source, "_failure_unsupported_function_IRC",
"Sorry, [_method] is not supported by the IRC gateway.",
([ "_method": mc ]) );
return 1;
}
// if (::msg(source, mc, data, vars) && abbrev("_message", mc)) {
// sollte 1 zurückgeben wenn die msg ok ist
#ifdef IRCGATE_HISTORY
"place/storic"::msg(source, mc, data, vars);
#else
"place/public"::msg(source, mc, data, vars);
#endif
// ein generischer renderer muss her.. das hier ist
// zu blöd so, erkennt keine varianten mit _action etc
if (abbrev("_message_public", mc)) {
#ifdef SERVER
// t = data || irctext( T(mca, 0), vars, data, source );
// send(source, "PRIVMSG #"+qChatChannel()+" :"+ t);
#else
if (stringp(source)) vars["_nick"] = source;
send(source, "PRIVMSG #"+qChatChannel()+" :"+
irctext( T(mca, 0), vars, data, source ));
#endif
}
else {
PT(("abgefangen: %O\n", mc))
}
#endif
return 1;
}
static chanop(arg) {
#ifndef SERVER
log_file("IRC_BOSS", "%O chops %O on %O\n", previous_object(), arg, ME);
emit("MODE #"+ qChatChannel()+ " +o "+arg+"\n");
#endif
return 1;
}
cmd(a, args, b, source, vars) {
string t;
// extra room commands for operators of the gateway
if (b > 0) switch(a) {
case "chop":
if (sizeof(args) == 2) chanop(args[1]);
return 1;
case "disc":
return disc();
#if !defined(SERVER) || DEBUG > 0
case "quote":
t = ARGS(1);
log_file("IRC_BOSS", "[%s] %O quotes %O\n",
ctime(), previous_object(), t);
emit(t+ "\n");
return 1;
#endif
}
return ::cmd(a, args, b, source, vars);
}
onEnter(source, mc, data, vars) {
if (interactive(ME)) tellChannel(source, mc, vars, data);
return ::onEnter(source, mc, data, vars);
}
leave(source, mc, data, vars) {
if (interactive(ME)) tellChannel(source, mc, vars, data);
return ::leave(source, mc, data, vars);
}
#endif

35
world/net/irc/gatebot.h Normal file
View file

@ -0,0 +1,35 @@
// $Id: gatebot.h,v 1.26 2008/02/01 15:56:50 lynx Exp $ // vim:syntax=lpc
//#define IRCGATE_HIDE
#ifdef UNIXUSER
# define IRCGATE_USERID UNIXUSER
#else
# define IRCGATE_USERID "psyced"
#endif
#define CHAT_CHANNEL
#define CHAT_CHANNEL_MODE "t"
//#define DEF_WHOMASK "*.symlynX.com"
#include "irc.h"
#undef NO_INHERIT
#ifdef SERVER
# ifndef IRCGATE_NICK
# define IRCGATE_NICK "PSYC.EU"
# endif
#else
# ifndef IRCGATE_NICK
# if CHATNAME == "psyco"
# define IRCGATE_NICK "PSYCgate"
# else
# define IRCGATE_NICK "|"+CHATNAME
# endif
# endif
#endif
#define URL_DOC "http://www.psyc.eu/ircgate"
#define IRCGATE_NAME "[PSYC] gateway * /m " IRCGATE_NICK " help"

3
world/net/irc/gateway.c Normal file
View file

@ -0,0 +1,3 @@
#define SERVER
//#undef CHAT_CHANNEL
#include "gatebot.c"

46
world/net/irc/ghost.c Normal file
View file

@ -0,0 +1,46 @@
// $Id: ghost.c,v 1.8 2007/08/27 16:54:13 lynx Exp $
//
// <lynX> ghost users serve the purpose of implementing all the features of
// a PSYC/Jabber identity for users of an IRC network whose identities are
// auth'd by a NickServ.
#define GHOST
#include <net.h>
#undef SERVER_HOST
#define SERVER_HOST IRCGATE_NICK
volatile object relay;
#include "user.c"
emit(string output) {
unless (relay) {
//relay = RELAY_OBJECT -> load();
relay = find_object(RELAY_OBJECT);
PT(("%O using relay %O\n", ME, relay))
unless (relay) return quit(); // we have a problem
}
relay->emit(output, remotesource);
}
#if 0
msg(source, mc, data, vars, showingLog, target) {
if (showingLog) return;
PT(("%O relaying %O from %O\n", ME, mc, source))
//vars["_nick_target"] = MYNICK;
#ifdef IRCGATE_CHANNELS
/* we need to remember this for QUIT handling */
if (abbrev("_echo_place_enter", mc))
unless (places[source])
places[source] = vars["_nick_place"];
#endif
relay->msg(source || previous_object(), mc, data, vars,
showingLog, MYNICK); // we need mynick here for ircgate
// instead of target
// we aren't calling ::msg() thus all user functions are ineffective
// as of now
return;
}
#endif

33
world/net/irc/hack.i Normal file
View file

@ -0,0 +1,33 @@
// vim:noexpandtab:syntax=lpc
// $Id: hack.i,v 1.27 2007/10/08 11:00:31 lynx Exp $
// hacky compatibility with old-school
// MYNICK returns * for non-identified users
// none of these should be used
//
// well, shouldn't we just define those?
pr(mc, fmt, a,b,c,d,e,f,g,h) {
IRCD( D("IRC»pr\n") );
// skipping printStyle
unless (fmt = T(mc, fmt)) return;
emit(sprintf(psyc2irc(mc, 0) +" "+ MYNICK +" :"+ fmt +"\n",
a,b,c,d,e,f,g,h));
}
p(fmt, a,b,c,d,e,f,g,h) {
string s = sprintf("NOTICE "+MYNICK+" :"+ fmt +"\n", a,b,c,d,e,f,g,h);
IRCD( D("IRC»p "+s) );
emit(s);
}
reply(num, rest) {
string s = sreply(num, rest);
IRCD( D("IRC»r "+s) );
emit(s);
}
sreply(num, rest) {
return SERVER_SOURCE + num + " " + MYNICK + " " + rest + "\n";
}

33
world/net/irc/irc.h Normal file
View file

@ -0,0 +1,33 @@
// $Id: irc.h,v 1.17 2008/01/16 10:59:30 lynx Exp $ // vim:syntax=lpc
//
#include <net.h>
#ifndef NO_INHERIT
inherit IRC_PATH "common";
#endif
#define easychannel2place(name) (index("+&#", name[0]) != -1 ? name[1..] : 0)
#define channel2place(name) (name[0] == '#' ? name[1..] : 0)
#define place2channel(name) ("#"+name)
#ifdef _flag_encode_uniforms_IRC
# define uniform2irc(UNIT) (v("verbatimuniform") == "off"? replace(UNIT, "@", "%") : UNIT)
#else
# define uniform2irc(UNIT) (UNIT)
#endif
#ifdef SERVER
# define SERVER_SOURCE ""
#else
# ifdef RELAY
# define SERVER_SOURCE ":" IRCGATE_NICK " "
# else
# define SERVER_SOURCE ":" SERVER_HOST " "
# endif
#endif
#define MAX_IRC_BYTES 512
#ifndef IRCD
# define IRCD D3
#endif

55
world/net/irc/library.i Normal file
View file

@ -0,0 +1,55 @@
// vim:noexpandtab:syntax=lpc
// $Id: library.i,v 1.22 2007/10/08 11:00:31 lynx Exp $
//
// this gets included into the system function library
// also known as "simul_efun" in mud-speak.
#define NO_INHERIT
#include "irc.h" // gets numeric codes
#include "error.h" // gets numeric codes
#include "reply.h" // gets numeric codes
// one global method mapping
volatile mapping p2i;
string psyc2irc(string mc, mixed source) {
mixed c;
#if 0
// the code below will no longer generate server notices for remote
// sources - fippo likes it better that way
if (!source || objectp(source)) source = SERVER_SOURCE;
// i bet irssi wont like the !*@* (the weird problem we had with it
// about artifical nickname changes for same ident...)
//else source = ":" + source + "!*@* ";
else source = ":" + source + " ";
#else
// unfortunately, "pidgin" displays the non-server notices in an
// very annoying manner - one new window for each friend...
// will have to postpone this change until pidgin is fixed
source = SERVER_SOURCE;
#endif
unless (p2i) p2i = ([ // sorted by psyc method
"_list_places_members" : RPL_NAMREPLY,
// asking for a prompt is an error in irc protocol
"_query_password" : ERR_PASSWDMISMATCH,
"_status_place_topic" : RPL_TOPIC,
"_error_unavailable_nick_place" : ERR_BANNEDFROMCHAN, // pretty close
//"_status_place_members" : RPL_NAMREPLY,
// not accurate, because it is the /motto command
//"_echo_set_description" : RPL_NOWAWAY,
//"_echo_set_description_none" : RPL_UNAWAY,
// not accurate, because it only reflects the existence of a text msg
//"_status_presence_description" : RPL_NOWAWAY,
//"_status_presence" : RPL_UNAWAY,
// and what's worse, it's not working.. let's try something else..
// i'll just add these messages to /irchere and /ircgone
"_echo_set_description_away" : RPL_NOWAWAY,
"_echo_set_description_away_none" : RPL_UNAWAY,
]);
if (c = p2i[mc]) return SERVER_SOURCE + c;
// if (abbrev("_notice_place_enter", mc)) c = "JOIN";
// else if (abbrev("_notice_place_leave", mc)) c = "PART";
return source +"NOTICE";
}

330
world/net/irc/reply.h Normal file
View file

@ -0,0 +1,330 @@
// $Id: reply.h,v 1.9 2006/01/13 20:28:57 lynx Exp $ // vim:syntax=lpc
//
// many useful and broken numerics have been added since the IRC RFCs
// were written. http://www.alien.net.au/irc/irc2numerics.html provides
// some orientation. we are implementing some things on experimental
// basis and only on prescription by adding -DIRC_BEYOND_RFC to your
// ldmud flags or #define IRC_BEYOND_RFC to your local.h
#ifdef IRC_BEYOND_RFC
// nothing right now.
#endif
#ifndef IRC_STRICTLY_RFC
# define RPL_ISUPPORT "005" // the de facto standard for server2client
// settings
#endif
// added manually. not in RFC 1459.
#define RPL_CHANNELMODEUNKNOWN "472" // Unsupported MODE
// automatically extracted from RFC 1459, chapter 6. thanks sique.
#define RPL_USERHOST "302" // Reply format used by USERHOST to list
// replies to the query list. The reply
// string is composed as follows: <reply>
// ::= <nick>['*'] '=' <'+'|'-'><hostname>
// The '*' indicates whether the client has
// registered as an Operator. The '-' or
// '+' characters represent whether the
// client has set an AWAY message or not
// respectively.
#define RPL_ISON "303" // Reply format used by ISON to list
// replies to the query list.
#define RPL_AWAY "301" // These replies are used with the AWAY
#define RPL_UNAWAY "305" // command (if allowed). RPL_AWAY is sent
#define RPL_NOWAWAY "306" // to any client sending a PRIVMSG to a
// client which is away. RPL_AWAY is only
// sent by the server to which the client
// is connected. Replies RPL_UNAWAY and
// RPL_NOWAWAY are sent when the client
// removes and sets an AWAY message.
#define RPL_WHOISUSER "311" // Replies 311 - 313, 317 - 319 are all
#define RPL_WHOISSERVER "312" // replies generated in response to a
#define RPL_WHOISOPERATOR "313" // WHOIS message. Given that there are
#define RPL_WHOISIDLE "317" // enough parameters present, the answering
#define RPL_ENDOFWHOIS "318" // server must either formulate a reply out
#define RPL_WHOISCHANNELS "319" // of the above numerics (if the query nick
// is found) or return an error reply.
// The '*' in RPL_WHOISUSER is there as
// the literal character and not as a wild
// card. For each reply set, only
// RPL_WHOISCHANNELS may appear more than
// once (for long lists of channel names).
// The '@' and '+' characters next to the
// channel name indicate whether a client
// is a channel operator or has been granted
// permission to speak on a moderated
// channel. The RPL_ENDOFWHOIS reply is
// used to mark the end of processing a
// WHOIS message.
#define RPL_WHOWASUSER "314" // When replying to a WHOWAS message, a
#define RPL_ENDOFWHOWAS "369" // server must use the replies
// RPL_WHOWASUSER, RPL_WHOISSERVER or
// ERR_WASNOSUCHNICK for each nickname in
// the presented list. At the end of all
// reply batches, there must be
// RPL_ENDOFWHOWAS (even if there was only
// one reply and it was an error).
#define RPL_LISTSTART "321" // Replies RPL_LISTSTART, RPL_LIST,
#define RPL_LIST "322" // RPL_LISTEND mark the start, actual
#define RPL_LISTEND "323" // replies with data and end of the
// server's response to a LIST command. If
// there are no channels available to
// return, only the start and end reply
// must be sent.
#define RPL_CHANNELMODEIS "324" // When sending a TOPIC message to
#define RPL_NOTOPIC "331" // determine the channel topic, one of two
#define RPL_TOPIC "332" // replies is sent. If the topic is set,
// RPL_TOPIC is sent back else RPL_NOTOPIC.
#define RPL_INVITING "341" // Returned by the server to indicate that
// the attempted INVITE message was
// successful and is being passed onto the
// end client.
#define RPL_SUMMONING "342" // Returned by a server answering a SUMMON
// message to indicate that it is summoning
// that user.
#define RPL_VERSION "351" // Reply by the server showing its version
// details. The <version> is the version of
// the software being used (including any
// patchlevel revisions) and the
// <debuglevel> is used to indicate if the
// server is running in "debug mode". The
// "comments" field may contain any
// comments about the version or further
// version details.
#define RPL_WHOREPLY "352" // The RPL_WHOREPLY and RPL_ENDOFWHO pair
#define RPL_ENDOFWHO "315" // are used to answer a WHO message. The
// RPL_WHOREPLY is only sent if there is an
// appropriate match to the WHO query. If
// there is a list of parameters supplied
// with a WHO message, a RPL_ENDOFWHO must
// be sent after processing each list item
// with <name> being the item.
#define RPL_NAMREPLY "353" // To reply to a NAMES message, a reply
#define RPL_ENDOFNAMES "366" // pair consisting of RPL_NAMREPLY and
// RPL_ENDOFNAMES is sent by the server
// back to the client. If there is no
// channel found as in the query, then only
// RPL_ENDOFNAMES is returned. The
// exception to this is when a NAMES
// message is sent with no parameters and
// all visible channels and contents are
// sent back in a series of RPL_NAMEREPLY
// messages with a RPL_ENDOFNAMES to mark
// the end.
#define RPL_LINKS "364" // In replying to the LINKS message, a
#define RPL_ENDOFLINKS "365" // server must send replies back using the
// RPL_LINKS numeric and mark the end of
// the list using an RPL_ENDOFLINKS reply.
#define RPL_BANLIST "367" // When listing the active 'bans' for a
#define RPL_ENDOFBANLIST "368" // given channel, a server is required to
// send the list back using the RPL_BANLIST
// and RPL_ENDOFBANLIST messages. A
// separate RPL_BANLIST is sent for each
// active banid. After the banids have been
// listed (or if none present) a
// RPL_ENDOFBANLIST must be sent.
#define RPL_INFO "371" // A server responding to an INFO message
#define RPL_ENDOFINFO "374" // is required to send all its 'info' in a
// series of RPL_INFO messages with a
// RPL_ENDOFINFO reply to indicate the end
// of the replies.
#define RPL_MOTDSTART "375" // When responding to the MOTD message and
#define RPL_MOTD "372" // the MOTD file is found, the file is
#define RPL_ENDOFMOTD "376" // displayed line by line, with each line
// no longer than 80 characters, using
// RPL_MOTD format replies. These should be
// surrounded by a RPL_MOTDSTART (before
// the RPL_MOTDs) and an RPL_ENDOFMOTD
// (after).
#define RPL_YOUREOPER "381" // RPL_YOUREOPER is sent back to a client
// which has just successfully issued an
// OPER message and gained operator status.
#define RPL_REHASHING "382" // If the REHASH option is used and an
// operator sends a REHASH message, an
// RPL_REHASHING is sent back to the
// operator.
#define RPL_TIME "391" // When replying to the TIME message, a
// server must send the reply using the
// RPL_TIME format above. The string
// showing the time need only contain the
// correct day and time there. There is no
// further requirement for the time string.
#define RPL_USERSSTART "392" // If the USERS message is handled by a
#define RPL_USERS "393" // server, the replies RPL_USERSTART,
#define RPL_ENDOFUSERS "394" // RPL_USERS, RPL_ENDOFUSERS and
#define RPL_NOUSERS "395" // RPL_NOUSERS are used. RPL_USERSSTART
// must be sent first, following by either
// a sequence of RPL_USERS or a single
// RPL_NOUSER. Following this is
// RPL_ENDOFUSERS.
#define RPL_TRACELINK "200" // The RPL_TRACE* are all returned by the
#define RPL_TRACECONNECTING "201" // server in response to the TRACE message.
#define RPL_TRACEHANDSHAKE "202" // How many are returned is dependent on
#define RPL_TRACEUNKNOWN "203" // the the TRACE message and whether it was
#define RPL_TRACEOPERATOR "204" // sent by an operator or not. There is no
#define RPL_TRACEUSER "205" // predefined order for which occurs first.
#define RPL_TRACESERVER "206" // Replies RPL_TRACEUNKNOWN,
#define RPL_TRACENEWTYPE "208" // RPL_TRACECONNECTING and
#define RPL_TRACELOG "261" // RPL_TRACEHANDSHAKE are all used for
// connections which have not been fully
// established and are either unknown,
// still attempting to connect or in the
// process of completing the 'server
// handshake'. RPL_TRACELINK is sent by any
// server which handles a TRACE message and
// has to pass it on to another server. The
// list of RPL_TRACELINKs sent in response
// to a TRACE command traversing the IRC
// network should reflect the actual
// connectivity of the servers themselves
// along that path. RPL_TRACENEWTYPE is to
// be used for any connection which does
// not fit in the other categories but is
// being displayed anyway.
#define RPL_STATSLINKINFO "211"
#define RPL_STATSCOMMANDS "212"
#define RPL_STATSCLINE "213"
#define RPL_STATSNLINE "214"
#define RPL_STATSILINE "215"
#define RPL_STATSKLINE "216"
#define RPL_STATSYLINE "218"
#define RPL_ENDOFSTATS "219"
#define RPL_STATSLLINE "241"
#define RPL_STATSUPTIME "242"
#define RPL_STATSOLINE "243"
#define RPL_STATSHLINE "244"
#define RPL_UMODEIS "221" // To answer a query about a client's own
// mode, RPL_UMODEIS is sent back.
#define RPL_LUSERCLIENT "251" // In processing an LUSERS message, the
#define RPL_LUSEROP "252" // server sends a set of replies from
#define RPL_LUSERUNKNOWN "253" // RPL_LUSERCLIENT, RPL_LUSEROP,
#define RPL_LUSERCHANNELS "254" // RPL_USERUNKNOWN, RPL_LUSERCHANNELS and
#define RPL_LUSERME "255" // RPL_LUSERME. When replying, a server
// must send back RPL_LUSERCLIENT and
// RPL_LUSERME. The other replies are only
// sent back if a non-zero count is found
// for them.
#define RPL_ADMINME "256" // When replying to an ADMIN message, a
#define RPL_ADMINLOC1 "257" // server is expected to use replies
#define RPL_ADMINLOC2 "258" // RPL_ADMINME through to RPL_ADMINEMAIL
#define RPL_ADMINEMAIL "259" // and provide a text message with each.
// For RPL_ADMINLOC1 a description of what
// city, state and country the server is in
// is expected, followed by details of the
// university and department
// (RPL_ADMINLOC2) and finally the
// administrative contact for the server
// (an email address here is required) in
// RPL_ADMINEMAIL.
#if 0
#define REPLY_MSGS ([\
RPL_USERHOST : ":%s",\
RPL_ISON : ":%s",\
RPL_AWAY : "%s :%s",\
RPL_UNAWAY : ":You are no longer marked as being away",\
RPL_NOWAWAY : ":You have been marked as being away",\
RPL_WHOISUSER : "%s %s %s * :%s",\
RPL_WHOISSERVER : "%s %s :%s",\
RPL_WHOISOPERATOR : "%s :is an IRC operator",\
RPL_WHOISIDLE : "%s %s :seconds idle",\
RPL_ENDOFWHOIS : "%s :End of /WHOIS list",\
RPL_WHOISCHANNELS : "%s :%s",\
RPL_WHOWASUSER : "%s %s %s * :%s",\
RPL_ENDOFWHOWAS : "%s :End of WHOWAS",\
RPL_LISTSTART : "Channel :Users Name",\
RPL_LIST : "%s %d :%s",\
RPL_LISTEND : ":End of /LIST",\
RPL_CHANNELMODEIS : "%s %s %s",\
RPL_NOTOPIC : "%s :No topic is set",\
RPL_TOPIC : "%s :%s",\
RPL_INVITING : "%s %s",\
RPL_SUMMONING : "%s :Summoning user to IRC",\
RPL_VERSION : "%d.%d %s :%s",\
RPL_WHOREPLY : "%s %s %s %s %s %s :%d %s",\
RPL_ENDOFWHO : "%s :End of /WHO list",\
RPL_NAMREPLY : "%s :%s",\
RPL_ENDOFNAMES : "%s :End of /NAMES list",\
RPL_LINKS : "<mask> <server> :<hopcount> <server info>",\
RPL_ENDOFLINKS : "<mask> :End of /LINKS list",\
RPL_BANLIST : "<channel> <banid>",\
RPL_ENDOFBANLIST : "",\
RPL_INFO : ":%s",\
RPL_ENDOFINFO : ":End of /INFO list",\
RPL_MOTDSTART : ":- %s Message of the day - ",\
RPL_MOTD : ":- %s",\
RPL_ENDOFMOTD : ":End of /MOTD command",\
RPL_YOUREOPER : ":You are now an IRC operator",\
RPL_REHASHING : "%s :Rehashing",\
RPL_TIME : "",\
RPL_USERSSTART : ":UserID Terminal Host",\
RPL_USERS : ":%-8s %-9s %-8s",\
RPL_ENDOFUSERS : ":End of users",\
RPL_NOUSERS : ":Nobody logged in",\
RPL_TRACELINK : "Link %s %s %s",\
RPL_TRACECONNECTING : "Try. <class> <server>",\
RPL_TRACEHANDSHAKE : "H.S. <class> <server>",\
RPL_TRACEUNKNOWN : "???? <class> [<client IP address in dot form>]",\
RPL_TRACEOPERATOR : "Oper <class> <nick>",\
RPL_TRACEUSER : "User <class> <nick>",\
RPL_TRACESERVER : "Serv <class> <int>S <int>C <server> \
<nick!user|*!*>@<host|server>",\
RPL_TRACENEWTYPE : "<newtype> 0 <client name>",\
RPL_TRACELOG : "File <logfile> <debug level>",\
RPL_STATSLINKINFO : "<linkname> <sendq> <sent messages> \
<sent bytes> <received messages> <received bytes> <time open>",\
RPL_STATSCOMMANDS : "<command> <count>",\
RPL_STATSCLINE : "C <host> * <name> <port> <class>",\
RPL_STATSNLINE : "N <host> * <name> <port> <class>",\
RPL_STATSILINE : "I <host> * <host> <port> <class>",\
RPL_STATSKLINE : "K <host> * <username> <port> <class>",\
RPL_STATSYLINE : "Y <class> <ping frequency> <connect \
frequency> <max sendq>",\
RPL_ENDOFSTATS : "<stats letter> :End of /STATS report",\
RPL_STATSLLINE : "L <hostmask> * <servername> <maxdepth>",\
RPL_STATSUPTIME : ":Server Up %d days %d:%02d:%02d",\
RPL_STATSOLINE : "O <hostmask> * <name>",\
RPL_STATSHLINE : "H <hostmask> * <servername>",\
RPL_UMODEIS : "<user mode string>",\
RPL_LUSERCLIENT : ":There are <integer> users and <integer> \
invisible on <integer> servers",\
RPL_LUSEROP : "<integer> :operator(s) online",\
RPL_LUSERUNKNOWN : "<integer> :unknown connection(s)",\
RPL_LUSERCHANNELS : "<integer> :channels formed",\
RPL_LUSERME : ":I have <integer> clients and <integer> \
servers",\
RPL_ADMINME : "<server> :Administrative info",\
RPL_ADMINLOC1 : ":<admin info>",\
RPL_ADMINLOC2 : ":<admin info>",\
RPL_ADMINEMAIL : ":<admin info>",\
])
#endif

179
world/net/irc/server.c Normal file
View file

@ -0,0 +1,179 @@
// $Id: server.c,v 1.85 2007/11/27 16:13:41 lynx Exp $ // vim:syntax=lpc
//
// IRC protocol receptionist
//
#define MYNICK "*"
#include "irc.h" // inherits net/irc/common
#include "server.h" // inherits net/server
#include "error.h" // gets numeric codes
#include "reply.h" // gets numeric codes
//#define emit(bla) binary_message(replace(bla, "\n", "\r\n"))
#include "hack.i"
volatile string login;
volatile string host;
volatile string name;
volatile string pass;
// for now it's a bit complicated to re-issue all joins and i'm not
// even sure if it would be enough for the irc clients.. so better
// just quit the old object and start anew.
keepUserObject(user) { return 0; }
qScheme() { return "irc"; }
qName() { return "*"; } // MYNICK? no i think i tried that before
// and it didn't work
void create() {
unless (clonep()) return;
sTextPath(0, 0, "irc");
}
createUser(nick) {
return named_clone(IRC_PATH "user", nick);
}
parse(a) {
::parse(a);
if (ME) next_input_to(#'parse);
}
promptForPassword() {
// string me;
//
// that's too complicated for dirty coded clients
// if (me = user->vQuery("me")) p(nick+" "+me);
unless (pass) w("_query_password", 0, ([ "_nick": MYNICK ]) );
return 1;
}
#ifdef PRO_PATH
# define BOUNCE_D NET_PATH "d/bounce"
#else // grummel
# define BOUNCE_D DAEMON_PATH "bounce"
#endif
morph() {
// das zeug hier gehört irgendwann mal in die textdb, eilt nich..
if (user && interactive(user) && BOUNCE_D->checkBounce(nick)) {
write("ERROR :Closing Link: " + nick
+ " (You've been bouncing in and out (2 or more clients try "
"to connect to this server using your identity). Please "
"stop all clients except the one you want to use for "
"chatting and (if needed) try to connect again in two "
"minutes. Thanks.)\n");
quit();
return;
}
#ifdef IRCPLUS
string lynx="/"; lynx[0] = 0x1c; // ctrl-backslash
# define SUPPORTS ":" SERVER_HOST " 800 "+nick+" IDENTITY :"+ \
"password"+ lynx +"email optional\n"
#else
# define SUPPORTS
#endif
// feeps says mIRC extracts its public ip from the 001 message
write("\
:" SERVER_HOST " 001 "+nick+" :Connected to an IRC emulation daemon on the [PSYC] network, "+ nick +"@"+ query_ip_name() +"\n\
:" SERVER_HOST " 002 "+nick+" :Your host is " SERVER_HOST " running version " SERVER_VERSION "\n\
" SUPPORTS "\
:" SERVER_HOST " 004 "+nick+" " SERVER_HOST " " SERVER_VERSION " * *\n");
// :" SERVER_HOST " 003 "+nick+" :This server was created long before you were born.\n\
// 005 is issued by user.c
unless (::morph()) return;
}
ircMsg(from, cmd, args, text, all) {
switch(cmd) {
case "server":
// welcher client tut das senden?
// eben kein client, sondern ein server mit C/N lines
write("402 :No server connectivity implemented.\n");
quit();
return 1;
case "user":
D1(case "u":)
if (sscanf(args, "%s %s %s", login, host, name) < 3) {
//reply(ERR_NEEDMOREPARAMS,
// ":Incorrect number of parameters");
}
// TODO: why is it using THIS syntax here?
login = "irc:"+login+"@"+host;
// unfortunately we have to go by USER since
// NICK may be reissued in a new connection and
// a password query would interfere with the PASS
// which is already coming our way before we know
if (nick) {
D2( D("irc:calling hello from USER\n"); )
hello(nick, login, pass);
}
return 1;
case "oper":
case "pass":
D1(case "p":)
pass = args || text;
// if (nick) password(args);
if (nick && pass) {
D2( D("irc:calling hello from PASS\n"); )
hello(nick, login, pass);
}
return 1;
case "nick":
D1(case "n":)
// bug in some clients like ircg.. they send NICK :nick
nick = args || text;
if (nick && (pass || login)) {
D2( D("irc:calling hello from NICK\n"); )
hello(nick, login, pass);
}
return 1;
}
return ::ircMsg(from, cmd, args, text, all);
}
quit() {
destruct(this_object());
return 1;
}
w(mc, data, vars, a,b,c,d) {
emit(psyc2irc(mc, 0) +" "+MYNICK+" :");
return ::w(mc, data, vars, a,b,c,d);
}
tls_logon(a) {
PT(("%O tls_logon\n", ME))
// PT(("tls_certificate info:\n%O\n", tls_certificate(ME, 1)))
next_input_to(#'parse);
::logon(a);
return 1;
}
logon(failure) {
if (this_interactive()) set_prompt(""); // case of failure?
next_input_to(#'parse);
#if __EFUN_DEFINED__(tls_query_connection_state)
if (tls_query_connection_state(ME) == 0) {
// DONT ::logon if this is to be done by tls_logon
::logon(failure);
}
#else
::logon(failure);
#endif
#ifdef _flag_log_sockets_IRC
log_file("RAW_IRC", "\nnew connection %O from %O\n",
ME,
# ifdef _flag_log_hosts
query_ip_name()
# else
"?"
# endif
);
#endif
return 1;
}

1228
world/net/irc/user.c Normal file

File diff suppressed because it is too large Load diff