// vim:foldmethod=marker:syntax=lpc:noexpandtab // $Id: library.i,v 1.172 2008/07/26 10:54:31 lynx Exp $ #include // #include "../base64.i" #define QUIT destruct(ME); return 1; #if SYSTEM_CHARSET != "UTF-8" # echo "FIXME: PSYC will be not be rendered in UTF-8." #endif volatile mapping obj2unl = ([]); volatile mapping unl2obj = ([]); // global location to identification resolver volatile mapping unl2uni = ([]); varargs void register_location(mixed unl, vamixed uni, vaint unsafe) { PROTECT("REGISTER_LOCATION") if (uni) { if (unsafe && unl2uni[unl]) { P1(("register_location denied: %O says it is %O, but that is already registered for %O\n", uni, unl, unl2uni[unl])) return; } unl2uni[unl] = uni; } else m_delete(unl2uni, unl); } #ifndef _flag_disable_module_authentication /** finds the _identification for a network source. if it cannot find ** the information in the cache, and a second argument is given with ** the _identification claimed by the source, that _identification ** will be asked for confirmation via PSYC. the result will obviously ** be available only at a later time. there is no callback function ** to this purpose. this function will ONLY return an identification ** when it is effectively different from the provided source. ** when a source is its own identification, this returns to avoid ** any extra processing. maybe one day we will return 1 if that's ** useful in any way, but you can check source == givenUNI yourself. ** the reason why the variable is called givenUNI and not identification ** is because a client may very well claim to be someone and actually not ** be that person. we are not sure about identification before the ** _request_authentication has returned. */ // this code is used intensely in FORK mode, whereas in !FORK, // since entity.c, the givenUNI is never passed, thus inactive. // unl2uni is only being used for local users' clients, not remotes // varargs mixed lookup_identification(mixed source, vamixed givenUNI) { PROTECT("LOOKUP_IDENTIFICATION") P2(("lookup_identification %O (%O) in %O\n", source, givenUNI, unl2uni)) if (member(unl2uni, source)) return unl2uni[source]; // ask a remote uni, but don't wait to complete if (givenUNI && source) { unl2uni[source] = 0; // ensures that we don't get here twice // now and in future always return 0 to avoid extra // processing of if (givenUNI == source) return 0; P1(("%O sending _request_authentication to %O for %O\n", ME, givenUNI, source)) # ifndef __PIKE__ // TPD sendmsg(givenUNI, "_request_authentication", 0, ([ "_location" : source ])); # endif } // we could look again ;) //if (unl2uni[source]) return unl2uni[source]; return 0; } #endif varargs string psyc_name(mixed source, vastring localpart) { P3((">> psyc_name(%O, %O)\n", source, localpart)) string s; if (s = obj2unl[source]) return s; #if DEBUG > 0 if (stringp(source)) raise_error("psyc_name called for string "+ source+ "\n"); #else if (stringp(source)) return source; #endif #ifdef PARANOID unless (objectp(source)) return 0; #endif unless (s = localpart) { if (s = source->psycName()) { if (abbrev("mailto:", s)) { unl2obj[s] = source; return obj2unl[source] = s; } } else s = file_name(source); } // // since this is an outgoing UNL and we do not do // virtual psyc hosting we should be // allowed to leave out the myUNL here. // since we need to recognize ourselves in _context messages // even if our _source has been repatched, we need to know // all our "canonical" names. so this isn't even enough. // we probably need to our canonical hostname: myUNLCN. // #ifndef __HOST_IP_NUMBER__ // if (myUNLIP) #endif // unl2obj[myUNLIP +"/"+ s] = source; // why store it with an ip? nobody uses that anyway!? // is it being used anywhere in the parser? naaah. s = myUNL + s; P3((">>> psyc_name for %O is %O\n", source, s)) unl2obj[s] = source; return obj2unl[source] = s; } object psyc_object(string uniform) { P3(("psyc_object(%O) in %O\n", uniform, unl2obj)) return unl2obj[uniform]; } object find_psyc_object(array(mixed) u) { P3((">> find_psyc_object(%O)\n", u)) string t, r, svc, user; object o; user = u[UNick]; r = u[UResource]; if (r && strlen(r)) { #if __EFUN_DEFINED__(trim) # include r = trim(r, TRIM_RIGHT, '/'); #else while (char_from_end(r, 1) == '/') r = slice_from_end(r, 0, 2); #endif if (strlen(r)) switch(r[0]) { case '^': break; case '~': #ifdef _flag_enable_module_microblogging if (u[UChannel]) { t = lower_case(r +"#"+ u[UChannel]); r = PLACE_PATH + t; if (o = find_object(r)) break; unless (t = legal_name(t, 1)) break; // untreated catch? interesting.. catch(o = r -> load(t)); } #endif break; case '$': // target wird auf serv/args gesetzt // weiss noch nicht ob das gut ist t = r[1..]; unless (sscanf(t, "%s/%s", svc, r)) svc = t; //P3(("find_service: %O for %O(%O)\n", o, svc, r)) if (o = find_service(lower_case(svc))) break; // unless (user) { // croak("_failure_unavailable_service", // "No service '[_service]' available here.", ([ "_service" : svc ]) ); // return restart(); // } return 0; // TODO! #ifndef __PIKE__ case '@': // t[0..0] = PLACE_PATH; r = PLACE_PATH + lower_case(r[1..]); if (o = find_object(r)) break; // should check for legal name instead // of catch. TODO catch(o = r -> load()); if (objectp(o)) break; // fall thru #endif default: o = find_object(r); D2( if(!o) D("OBJECT NOT FOUND: "+ r +"\n"); ) } } else unless (user) { //return 0; // return SERVER_UNIFORM !? return find_target_handler("/"); } if (!objectp(o) && user) { // psyc/parse used to do this here o = summon_person(user); //, PSYC_PATH "user"); P2(("%O summoning %O: %O\n", ME, user, o)) } P3((">>> found psyc object: %O\n", o)) return o; } #ifndef __PIKE__ // library transmits udp packets itself #include "edit.i" #endif // target is lowercased already int psyc_sendmsg(mixed target, string mc, mixed data, mapping vars, int showingLog, mixed source, array(mixed) u) { string sname, host, buf, room; int port, usesrv = 1; object ob; mixed t; unless (u[UHost]) { #ifndef __PIKE__ // TPD sendmsg(source, "_failure_unsupported_notation_location", "PSYC server-independent uniform network " "identificators not implemented yet.", 0, 0); #endif return 0; } #if DEBUG > 0 unless (stringp(mc)) { P0(("psyc_sendmsg got mc == %O from %O.\n" "probably a mistake when converting a walk_mapping.\n", mc, previous_object())) return 0; } #endif sname = objectp(source) ? psyc_name(source) : sname; // host = lower_case(u[UHostPort]); // unless (u[URoot]) u[URoot] = "psyc://"+host; // TODO: vars should be enforced system-wide unless (mappingp(vars)) vars = ([ ]); // seid ihr euch wirklich sicher, dass ihr diese zeile entfernen wollt? vars["_target"] = target; // this could help protect against sources who destruct while // the message is in the queue. is it being used or is it okay to do // this operation at enqueing time? in fact the templates who use it // get it from psyctext() which resolves _source as source.. so this // here should not be necessary.. then again we just calculated it // so we don't want it to be calculated again later.. so we should // rather adapt the other spots where it's used rather than here.. // after all it's mostly speed improvements.. no big thing.. TODO vars["_source"] = sname; unless (port = u[UPort]) port = u[UTransport] == "s" ? PSYCS_SERVICE : PSYC_SERVICE; else { usesrv = 0; if (port < 0) { P2(("Minusport in %O or %O, _failure to %O || %O\n", target, u[UHost], vars["_context"], source)) #ifndef __PIKE__ // TPD sendmsg(vars["_context"] || source, "_failure_network_connect_invalid_port", "No connectable port known for [_source_relay].", ([ "_source_relay" : target || u[UHost] ]), ME); // ME shouldn't be necessary! TODO #endif return 0; } } host = lower_case(u[UHost]); if (query_udp_port() == port && is_localhost(host)) { // this happens when a psyc client sends to a local // target that hasn't been incarnated yet... ob = find_psyc_object(u); // cache the resulting object for the url if (ob) { P2(("psyc_sendmsg registering %O for %O found by parsing uniform\n", target, ob)) register_target(target, ob); } #ifndef __PIKE__ // TPD return sendmsg(ob, mc, data, vars, source); #endif // or deliver directly? } // since the local URoot is in targets and pointing to net/root // we have to get here *after* making sure we are not addressing // a local entity. P3(("looking for %O(%O) in %O..\n", target, u[UHostPort], targets)) // if (member(targets, t2 = u[URoot])) { // t = targets[t2]; // if (t) { if (t = targets[u[URoot]]) { P2(("%O to be delivered on %O's circuit\n", target, query_ip_name(t) || t )) register_target(target, t); // was delivermsg( return t->msg(sname, mc, data, vars, 0, target); // no more cleansing of targets mapping.. see also net/library.i // } else { // P1(("PSYC/TCP target %O gone!\n", t2)) // m_delete( targets, t2 ); // } } #ifndef __PIKE__ // okay so we have no existing connection.. // current rule of thumb to decide whether to open up a tcp // also all localhost traffic should be handled by udp TODO #ifdef _flag_enable_routing_UDP if (u[UTransport] == "c" || # ifdef _flag_enable_routing_TLS u[UTransport] == "s" || # endif (!u[UTransport] && !abbrev("_notice", mc))) #else if (!u[UTransport] || u[UTransport] == "c" # ifdef _flag_enable_routing_TLS || u[UTransport] == "s" # endif ) #endif { dns_resolve(host, (: object o; string hopo = $2; string psychopo; string psycippo = "psyc://"+ $1 +"/"; // if ($3 && $3 != PSYC_SERVICE) { if ($9) { hopo += ":"+$9; psycippo += ":"+$9; } // hope it is correct to have a trailing slash here psychopo = "psyc://"+ hopo +"/"; unless (o = find_target_handler(psycippo)) { // we use psyc:host:port as an object name // we can change that if it is confusing #ifdef QUEUE_WITH_SCHEME // this makes it look for psyc:host:port as its queue o = ("psyc:"+hopo) -> circuit($2, $3, u[UTransport], usesrv && "psyc", 0, systemQ, psychopo); #else // this makes it look for host:port as its queue o = ("psyc:"+hopo) -> circuit($2, $3, u[UTransport], usesrv && "psyc", hopo, systemQ, psychopo); #endif //__PIKE__ // SRV tag "psyc" is actively being checked, but // don't rely on it.. just use it as a fallback if // nothing else is possible, but some clients may // no longer be able to connect to you... } register_target($4, o); register_target(psychopo, o); register_target(psycippo, o); P2(("delivering %O(%O) over %O\n", hopo, $1, o)) P3(("targets = %O\n", targets)) // psyc/circuit.c will register psyc://IP:PORT at logon() // the last thing missing is a way to register all CNAMEs return o->msg($5, $6, $7, $8, 0, $4); // ip=1 2 3 4 5 6 7 8 9 :), host, port, target, sname, mc, data, vars, u[UPort] ); return 1; } else #ifdef _flag_enable_routing_UDP if (u[UTransport] && u[UTransport] != "d") #endif { P1(("Invalid transport %O in %O, _failure to %O || %O\n", u[UTransport], target, vars["_context"], source)) sendmsg(vars["_context"] || source, "_failure_network_connect_invalid_transport", "Unknown transport type '[_transport]' for [_source_relay].", ([ "_source_relay" : target || u[UHost], "_transport": u[UTransport] ]), ME); // ME shouldn't be necessary! TODO return 0; } #ifdef _flag_enable_routing_UDP // fall thru to UDP # if DEBUG > 1 if (port) { D(S("UDP[%s:%d] <= %O: %s\n", host, port, source, mc)); } else { D(S("UDP[%s] <= %O: %s\n", host, source, mc)); } # endif #ifndef NEW_RENDER // this produced wrong _length. please discontinue. if (stringp(data) && strlen(data)) // why do we take guesses at data here? uncool! data="\n"+data+ (data[strlen(data)-1] == '\n' ? S_GLYPH_PACKET_DELIMITER "\n" : "\n" S_GLYPH_PACKET_DELIMITER "\n"); else data="\n" S_GLYPH_PACKET_DELIMITER "\n"; // TODO? look up textdb. // look! MMP-conformant support of the _context variable! if (room = vars["_context"]) { // this may have to change into a full psyc: URL if (objectp(room)) room = psyc_name(room); buf = S_GLYPH_PACKET_DELIMITER "\n" # ifdef PRE_SPEC ":_source\t"+ sname +"\n" # else ":_source_relay\t"+ sname +"\n" # endif + ":_context\t"+ room +"\n"; } else buf = S_GLYPH_PACKET_DELIMITER "\n" ":_source\t"+ sname +"\n" ":_target\t"+ target +"\n"; t = psyc_render(source, mc, data, vars, showingLog, target); unless (t) return 0; buf += t; #else buf = psyc_render(source, mc, data, vars, showingLog, target); unless (buf) return 0; #endif /* NEW_RENDER */ // host seems to already be in lower_case if (is_localhost(host)) return send_udp(host, port, buf); PT(("dns_resolve + send_udp %O:%O packet:\n%s", host,port,buf)) dns_resolve(host, (: if (stringp($1)) send_udp($1, $2, $3); else // if your driver complains about vars being // undefined at this point, you are probably // running an ldmud 3.2 which is not compatible // with psyced. pick a newer major version pls sendmsg(vars["_context"] || source, "_failure_network_send_resolve", "[_source_host] for [_source_relay] does not resolve.", ([ "_source_host" : host, "_source_relay" : target||host ]), ME); return; :), port, buf); return 1; #endif /* _flag_enable_routing_UDP */ #endif // PIKE } /* this breaks /connect ... object createUser(string nick) { PT(("creating " PSYC_PATH "user for "+ nick +"\n")) return named_clone(PSYC_PATH "user", nick); } */