ircproxy The Ultimate Cyborg |
The entry point for accessing data is the singleton Application.
This singleton can be accessed through Application::instance()
. If this code can be reached before main()
then instead Application::instantiate()
should be used, but this is not normally the case.
The Application gives access to two lists of objects through the following methods:
All known IRC networks are stored in Application::M_networks. This list of Network objects can be accessed through the member function Application::instance().networks()
.
A network is determined upon connection to a server by decoding the 001 numeric reply. FIXME: 005 NETWORKS= should be used if available.
The Network gives access to two lists of objects through the following methods:
An IRCproxy Identity represents a unique virtual identity, a different IRC personality, even though the real person behind it could be the same person of course. Information related to a real person is stored in an Account.
All identies are stored in Application::M_identities. This std::map
stores all Identity objects as function of their name (a std::string
). The map can be accessed through the member function Application::instance().identities()
.
An identity is determined by the first word in the PASS
message as received from the client. This string is also used as login to any service (for example X on Undernet). The full format of the PASS
message is:
PASS <login/identity> <ircproxy authentication password> <service password> <server password>
FIXME: Knowing the Identity, one can get the current ServerConnection by calling Identity::server_connection. This object is owned by the Identity object (and therefore has the same life time). The actual server connection is a ServerSession object that is created and managed by this object. The ServerSession can be obtained by a call to ServerConnection::server_session (or the shortcut Identity::server_session). Administrative information about the specific server itself (name, network, ports etc) is stored in a Server object, which can be obtained by a call to ServerConnection::server.
Input messages have the type MessageIn, while output messages have the type MessageOut.
A raw message often has a prefix (the source), for example, :nick!user@host JOIN #foo
or :server.name.org NOTICE nick :This is a notice
, where :nick!user@host
and :server.name.org
are the prefix of these respective messages. A prefix is represented by the Prefix class. Use a Prefix object whenever you need to deal with a prefix. The Prefix class has the notition of an "empty prefix" (no prefix at all), so that in many cases you don't have to treat that as a special case.
A prefix can be extracted from a MessageIn with:
Prefix prefix(msgin);
The type of the command (or numeric) is an int
and can be extracted from the MessageIn by calling MessageIn::key. Any subsequent token can be retrieved as string with MessageIn::param(n), n
starting at 0 for the first parameter after the command or numeric, or with MessageIn::params(n) to obtain parameter n
and higher as a single object (MsgPart).
There are two types of sessions: ClientSession and ServerSession. Each represents a TCP/IP connection (a socket) with an IRC client and server respectively. Although it is possible to write directly to these objects (they inherit from std::ostream
) you should never do that: all messages to these sockets should go through their respective queues so that ircproxy is able to keep track of how many messages can be bursted before flood control takes effect, do priority ordering in that regard, and last but not least to make sure you never reveal your hostname-- for example by joining a channel-- before your hostname is hidden.
The correct way to send a message is by constructing a MessageOut object and passing that to the session with session.queue_msg(msgout);
.
An IRC server is a server that the client succesfully connected with in the past and that belongs to some Network. Server::M_hostname (returned by Server::hostname) is the FQDN that the user passed to ircproxy by answering the "What server should I connect to?" question. Server::M_irc_name (returned by Server::get_irc_name) is the server name as received from the server over IRC after a connect (the prefix of the 001 numeric).
Knowing the server, one can find the Network through Server::network.
A Target is a base class for different types of targets, currently Nick and Channel, but also the private targets NoticeTarget, PrivateTarget and QuestionTarget. Targets exist solely as floating objects, pointed to by boost::shared_ptr<Target>
objects. When all shared pointers to a target are deleted, the target is destroyed.
Non-private Targets can have two ID strings: one for the client side and one for the server side. The distinction between client and server side is mainly because it allows translation of target names. Private targets (all of which are channels at the moment) only live on the client side, and therefore only have one ID string. Moreover, they don't change name.
There are two target_hold_map_type
maps that store shared pointers to channels: ServerSession::joined_channels
and ClientSesssion::joined_channels
that store boost::shared_ptr<Target>
pointers. The targets on those maps are updated to reflect the notion of server and client respectively about what channels the client has joined; whenever a JOIN/PART/KICK is received from the server or client. They are not updated when such a message is sent to the server or client. These maps match keys IRC-case insensitive.
The server side name of channels never changes and is equal to Target::serverside_name. This name is used as key on the server side map. If the channel is using translation, and thus has a different name as seen by the client, then a different key is used on the client side. This name is equal to Target::clientside_name.
Non-private Network objects maintain a map with type target_lookup_map_type
which store boost::weak_ptr<Target>
pointers. These pointers are automatically removed when the Target is destroyed. The key used for this map is the server side name.
By default, ircproxy supports three types of events:
Independ on the type of event, the class who's member function is used as call back function must have event_client_ct
as virtual base class.
class Object: public virtual event_client_ct { ... public: Object() : event_client_ct() { }
The destruction of such an object then automatically cancels the event request.
It is possible to have ircproxy call a member function of some object after a given time interval has elapsed.
The time to wait is stored in a timeval
. For example, in order to wait 1 second before calling Object::call_back_function
, one would write,
timeval delay = { 1, 0 }; timerRequest(delay, object, &Object::call_back_function);
The member function Object::call_back_function
might start something like:
void Object::call_back_function(timer_event_type_ct const& LIBCW_UNUSED_UNLESS_DEBUG(expired_at)) { DoutEntering(dc::debug, "Object::call_back_function(" << expired_at.get_expire_time() << ")");
It is possible to have ircproxy call a member function of some object if a certain message is received. Construct a MatchRequest object and pass it to the correct event server. For example,
server_session().message_event_server()(match_request, object, &Object::call_back_function);
would cause object.call_back_function
to be called, passing a MatcherEventType object as argument, as soon as the match_request
is fullfilled. See Match requests for information about how to construct a match request.
The member function Object::call_back_function
might start something like:
void Object::call_back_function(MatcherEventType const& event_type) { DoutEntering(dc::debug, "Object::call_back_function(" << event_type << ")");
At this point event_type.message()
is a reference to the MessageIn that did match the request.
A MatchRequest is an object representing a match request for some IRC message. The constructor takes the Identity that is issuing the request. Subsequently a series of member function can be called to further restrict what message will match. The first member function always must be operator()(int command)
, where command
is the key or numeric of the message to be matched. For a complete list see the member functions of MatchRequest.
For example,
MatchRequest match_request(identity);
match_request(keys::MODE).mynick(0); // MODE <my nick>
match_request(396).localserver(prefix).mynick(0). // <local server> 396 <my nick> <login/identity>[A-Za-z.]* :is now your hidden host regexp(M_identity->key() + "[A-Za-z.]*", 1). exactmatch("is now your hidden host", 2);
It is possible to have ircproxy call a member function of some object if some boolean expression becomes true. The boolean expression has to consist of Watched variables: variables of type Watched<T>
. Constants used in the watched boolean expressions must be wrapped too with ConstExpression<T>(value)
. You cannot use Watched<bool>
, instead use the special class Bool
. The Watched variables can be used as if they are not wrapped, but when using them in an expression that is passed to ON_TRUE
you have to use the dereference operator (operator*
). The type of the complete expression reflects the entry expression. It is therefore not feasible to store such an expression in a temporary variable. Best practise is to pass the expression immediately, as-is, to the macro ON_TRUE
.
For example, to have ircproxy call the member function call_back_function
of an Object object, one could do:
Bool var1 Watched<T1> var2; Watched<T1> var3; ... T1 const m = 1; ConstExpression<T1> mask(m); ON_TRUE(*var2 & *var3 == mask && *var1, object, &Object::call_back_function);
which would (re)evaluate the expression each time var1
, var2
or var3
change value. When the expression changes from false to true, then the member function object.call_back_function
is called.
The member function Object::call_back_function
might start something like:
void Object::call_back_function(ExpressionEventType const& LIBCW_UNUSED_UNLESS_DEBUG(event_type)) { DoutEntering(dc::notice, "Object::call_back_function(" << event_type << ')'); ASSERT(event_type); // Should be true, of course.
where event_type
is convertible to a boolean and reflects the changed value of the expression.
The application keeps certain data over restarts by storing objects in an XML file and rereading them at start up.
The process of tranforming the value of an object into a character stream that can be written to, and read from, a file, is called "serialization". In principle only a single object is serialized: the Application singleton. Every other object is subsequently serialized as part of the application object.
In order to add support for serialization of a class, a method serialize
has to be added. For example in order to add serialization support to a class Foo, one would write:
// The header file: class PersistXML;
class Foo { private: some_type M_member1; some_type M_member2; ... public: Foo(void); // Default constructor must exist. void serialize(PersistXML& xml); };
// The source file: #include "PersistXML.h"
void Foo::serialize(PersistXML& xml) { xml.serialize("M_member1", M_member1); xml.serialize("M_member2", M_member2); ... }
Where some_type
can be builtin types, or a custom class that also supports serialization through it's own serialize member function. It is not necessary to serialize every member. When reading an object back from file, the object is first constructed with the default constructor and subsequentially each builtin type is assigned the value as read from the XML file.
PersistXML::serialize
is overloaded for certain special types, providing an immediate support for the otherwise complex way of serialization. The special types currently supported are:
boost::shared_ptr<T>& std::vector<T>& std::list<T>& std::map<KEY, INFO, COMPARE>&
Copyright © 2005-2007 Carlo Wood. All rights reserved. |
---|