Changing NICK programmatically

These are old archives. They are kept for historic purposes only.
Post Reply
krbvroc1
Posts: 7
Joined: Tue Mar 29, 2005 4:49 am

Changing NICK programmatically

Post by krbvroc1 » Tue Mar 29, 2005 9:00 pm

I'm looking for some pointers on how to change a NICK from a module.

I'm using the IRC server in a single host environment pretty much for a chat room. When someone logs in (via the HOOK for local pre connection), I look up their user info in a db4 file and authenticate them. Once piece of info in that db4 file is a nick. It is guaranteed to be unique and cannot conflict with others (there are no remote servers involved since I'm running a single host).

I've look around the code and am not sure how to approach this challenge. I'm not running any services and don't think I will.

Can I modify the nick info somehow during the local pre connect hook?

Can I hook some other place like local connect and within the hook call 'm_nick' to change the persons nick upon login to the value in my db4 file?

Is there a way for a hook to 'send a command' as if the user sent it?

For users connecting via an applet on my webpage, I set their nick correctly in the applet param tags before the applet connects. However, I'd like to allow people who want the flexibility of using a standalone IRC client to be supported. For those, I have no control on how they configure their nick in their client so when the login, I want to immediately change their nick based on my db4 file.

Any suggestions on approaches to coding such a thing? Thanks.

Stealth
Head of Support
Posts: 2086
Joined: Tue Jun 15, 2004 8:50 pm
Location: Chino Hills, CA, US
Contact:

Post by Stealth » Wed Mar 30, 2005 12:27 am

Have a look at the m_svsnick module. I believe it calls m_nick to change the nick.

krbvroc1
Posts: 7
Joined: Tue Mar 29, 2005 4:49 am

Post by krbvroc1 » Wed Mar 30, 2005 12:41 am

Stealth wrote:Have a look at the m_svsnick module. I believe it calls m_nick to change the nick.
That is one of the files I've been looking at. However, It does not call m_nick, but I'm thinking that if I emulate what it does, it might work.

Initially I tried call m_nick from a hook function in my own module, but I could not call a module from a hook function. When loading ircd, I was getting 'undefined m_nick'. I could not get past that error. I did not see other code that had one module calling another so maybe I do not understand the architecture enough.

Right now, I'm putting something close to the m_svsnick code into a hookfunction. I am hooking HOOKTYPE_LOCAL_CONNECT. I'm running some tests now.

codemastr
Former UnrealIRCd head coder
Posts: 811
Joined: Sat Mar 06, 2004 8:47 pm
Location: United States
Contact:

Post by codemastr » Wed Mar 30, 2005 1:47 am

Initially I tried call m_nick from a hook function in my own module, but I could not call a module from a hook function. When loading ircd, I was getting 'undefined m_nick'. I could not get past that error. I did not see other code that had one module calling another so maybe I do not understand the architecture enough.
You can make a module call functions in another module, but it's a bit tricky. But I would definitely not recommend this approach because I believe it will lead to infinite recursion (m_nick calls register_user calls your hook which calls m_nick which calls register_user ad infinitum)

I'd just suggest changing it yourself from within a hook.
-- codemastr

aquanight
Official supporter
Posts: 862
Joined: Tue Mar 09, 2004 10:47 pm
Location: Boise, ID

Post by aquanight » Thu Mar 31, 2005 6:51 am

Wouldn't the client need to be notified of the new nick somehow? Or has the 001 and such not yet been sent when the hook is run?

krbvroc1
Posts: 7
Joined: Tue Mar 29, 2005 4:49 am

Post by krbvroc1 » Thu Mar 31, 2005 5:01 pm

aquanight wrote:Wouldn't the client need to be notified of the new nick somehow? Or has the 001 and such not yet been sent when the hook is run?
I tried to do this two ways.

1) The most correct way to me was that I hooked the LOCAL_CONNECT. In the hook, I performed the code in the m_sysnick to change the nick. That routine sends out a 'NICK' command to common channels and updates the 'watch' hash table.

This scheme worked if the client issued a PASS, NICK, USER on connect. However, if the client changed the order and issued a PASS, USER, NICK I had a problem. In the connect callback I have to call 'find_person()' to get a pointer to the user since the connect hook only receives a sptr. In the the case of NICK coming last, find_person failed to find the user so I had no way to execute most of the code in m_sysnick which modifes that structure.

I have no idea why the find_person would fail. The LOCAL_CONNECT should happen after register user regardless of order of USER, NICK commands by the client during login.

2) I ended up just hooking PRE LOCAL CONNECT. In there, I just copy over the nick. That happens before the 001 and the client seems to get the be aware that its nick changed.

I would have preferred #1 because it seems like a clean approach. #2 is more a hack but its the only one that works with all local clients (those that send USER, NICK in any order).

Lastly, I used the 'Unoccupied 4 flag' as a 'authenticated user' flag. Since the code clears out the password (i assume so it doesn't stay in memory long), there is no way to know if the user logged in with a password or not. I overrode NICK and after I authenticate I do not allow nick changes. This allows NICK to work during the login sequence but not after. It also lets me output a 'This server requires a password' if the user forgets to configure their client.

The only problem I've seen so far with this setup is that if I do a 'rehash' I get a core dump. It does not like rehash when I've done a command override on the 'nick'. If I simply remove the AddOverride from the 'rehash_complete()' and remove the 'DelOverride' from MOD_UNLOAD rehash works.

codemastr
Former UnrealIRCd head coder
Posts: 811
Joined: Sat Mar 06, 2004 8:47 pm
Location: United States
Contact:

Post by codemastr » Thu Mar 31, 2005 11:27 pm

krbvroc1 wrote: n the connect callback I have to call 'find_person()' to get a pointer to the user since the connect hook only receives a sptr.
I have *no* idea what this means at all. "a sptr" sptr is just a variable name. It could just as easily be called "purpledog." The variable passed as sptr is an aClient structure that represents the connecting user. Find_person returns this exact same structure. If you compare the return value of find_person to the value of sptr, they should match. So I have no clue what any of this comment means.
-- codemastr

krbvroc1
Posts: 7
Joined: Tue Mar 29, 2005 4:49 am

Post by krbvroc1 » Fri Apr 01, 2005 12:05 am

codemastr wrote: I have *no* idea what this means at all. "a sptr" sptr is just a variable name. It could just as easily be called "purpledog." The variable passed as sptr is an aClient structure that represents the connecting user.
From what I can tell the aClient has a dual role. It can describe either a server/connection or a user. The same structure is used for both purposes and different fields are filled in depending on the context. The coding convention seems to be an 'sptr' refers to an aClient structure that holds information about a server/connection whereas an 'aptr' or 'cptr' refers to an aClient structure that holds information about a user.

That is what I gleaned from the code.

It seemed to be that the 'aClient' structure (called sptr) that is passed to the LOCAL_CONNECT hook contains connection info, not user info. Other hooks (such as PRE LOCAL CONNECT) get passed TWO aClient structures--one for the connection and one for user.

Does this make sense? I must be misunderstanding something here.

What is the purpose for some hooks receiving two aClient structures in their arg list whereas others only one? (PRE CONNECT vs CONNECT)
codemastr wrote: Find_person returns this exact same structure. If you compare the return value of find_person to the value of sptr, they should match. So I have no clue what any of this comment means.
What I found was that during LOCAL_CONNECT for the case where
the IRC client send PASS, NICK, USER (in that sequence) calling find_person with 'sptr->name' is sucessful. In the case wehre the ICR client sends 'PASS, USER, NICK' find_person(sptr->name) returns NULL.
I assumed that I had to call find_person to get the user info. I assume this because m_svsnick.c does this. I need to call it to be sure I dont get a collision.

codemastr
Former UnrealIRCd head coder
Posts: 811
Joined: Sat Mar 06, 2004 8:47 pm
Location: United States
Contact:

Post by codemastr » Fri Apr 01, 2005 6:21 am

The coding convention seems to be an 'sptr' refers to an aClient structure that holds information about a server/connection whereas an 'aptr' or 'cptr' refers to an aClient structure that holds information about a user.
That couldn't be more wrong! The IRCd makes no differentiation between a server and a user. They are both clients. sptr means "sender pointer" cptr means "client pointer" and there is no aptr, it's acptr which means "affected client pointer." NOTHING says that any particular one must be a server and others must be a user. In fact, you will notice that in *many* places (in the same function even) we do if (IsServer(sptr)) ... else if (IsPerson(sptr)) that's because sptr can be either (or even other things in some cases)
It seemed to be that the 'aClient' structure (called sptr) that is passed to the LOCAL_CONNECT hook contains connection info, not user info.
I have no idea what you mean by "connection info." The sptr passed to the hook is the exact same one that find_person is going to return to you. If you do if (sptr == find_person(sptr->name)) you'll see that it is always true, it's going to return sptr!
Other hooks (such as PRE LOCAL CONNECT) get passed TWO aClient structures--one for the connection and one for user.
Once again, I have no idea what you are talking about. HOOKTYPE_PRE_LOCAL_CONNECT is passed just one parameter, aClient *sptr.
What is the purpose for some hooks receiving two aClient structures in their arg list whereas others only one? (PRE CONNECT vs CONNECT)
There would be no purpose, hence this is why we don't send two!
I assume this because m_svsnick.c does this. I need to call it to be sure I dont get a collision.
I haven't the slightest clue what a collision has to do with anything here. That makes the least sense out of everything you said. But, had you actually looked at what svsnick is doing, it's quite obvious why it calls find_person, it doesn't have an aClient!. It is taking a nickname supplied by the user (the first parameter to the svsnick command) and is looking up the aClient struct associated with it. In your case, you already have the aClient struct, so looking it up makes absolutely no sense at all.
-- codemastr

krbvroc1
Posts: 7
Joined: Tue Mar 29, 2005 4:49 am

Post by krbvroc1 » Fri Apr 01, 2005 7:51 am

codemastr wrote: That couldn't be more wrong! The IRCd makes no differentiation between a server and a user. They are both clients. sptr means "sender pointer" cptr means "client pointer" and there is no aptr, it's acptr which means "affected client pointer." NOTHING says that any particular one must be a server and others must be a user. In fact, you will notice that in *many* places (in the same function even) we do if (IsServer(sptr)) ... else if (IsPerson(sptr)) that's because sptr can be either (or even other things in some cases)
First, keep in mind in mind that I'm trying to understand code I didn't write and which there isn't much up to date documentation for (at a programmer level). I appreciate your feedback. I've only been at this for 3 days.

I looked at the code again (rather than trying to recall from memory in my last post) and here is what is causing my confusion - the CMD_FUNC which seems to be the definition of commands. The CMD_FUNC is
(aClient *cptr, aClient *sptr, int parc, char *parv[])

There are TWO aClient pointers passed in. Taking for example, m_nick. In that command sometimes cptr is used other times sptr is used. When I crossed reference to struct.h and the definition for aClient, I saw the comments for the two fields:

anUser *user; /* ...defined, if this is a User */
aServer *serv; /* ...defined, if this is a server */

Because of the dual pointers passed via the CMD_FUNC and the comments about a user or a server, I was confused. That is why I thought the same structure was used for two purposes.

Why are two pointers passed in to commands? When should one be used vs the other?

Another routines that confused me while trying to understand the code is exit_client() which takes THREE aClient pointers. Some code passed the same pointer to all three, some pass NULL to one and something to the others, some modules pass a cptr and an acptr, etc.

Can you see my confusion?
If you do if (sptr == find_person(sptr->name)) you'll see that it is always true, it's going to return sptr!
For some reason, depending on the order of the PASS, NICK, USER I'm not getting that result if I do that in LOCAL CONNECT.
I haven't the slightest clue what a collision has to do with anything here. That makes the least sense out of everything you said. But, had you actually looked at what svsnick is doing, it's quite obvious why it calls find_person, it doesn't have an aClient!. It is taking a nickname supplied by the user (the first parameter to the svsnick command) and is looking up the aClient struct associated with it. In your case, you already have the aClient struct, so looking it up makes absolutely no sense at all.
I definitely look at svsnick, I just came to the wrong conlusion because of my flawed thinking about the two aClients. I was confusing the CMD_FUNC with a hook. Sorry about that.

I can explain why I am calling find_user however. I have to protect againt the same person logging in more than once. If a user logs in, I look them up in my db4 database file, and retreive their handle. If they log in a second time rather than rejecting the connection with a 'nick in use', I append a unique number to the nick in the database. So I put 'find_person' in a loop looking for the next available nick in a sequence. my_nick[1], my_nick[2], my_nick[3]. That is the reason I call find_person and didn't want to blindly use the sptr passed in.

codemastr
Former UnrealIRCd head coder
Posts: 811
Joined: Sat Mar 06, 2004 8:47 pm
Location: United States
Contact:

Post by codemastr » Fri Apr 01, 2005 5:08 pm

Why are two pointers passed in to commands? When should one be used vs the other?
This requires a bit of knowledge about how IRC works. Lets look at it this way, I am on server1.blah and you are on server2.blah (they are linked together). I want to send you a privmsg. So the command I send is "PRIVMSG krbvroc1 :hello". My server receives this command from me. It realizes codemastr sent the command and it is addressed to krbvroc1. At this point, it realizes you are not on server1, you are on server2. Therefore, it must pass the message on to server2, then server2 will send the message to you. In that example, we have two situations. On server1, sptr == cptr == codemastr. On server2, sptr == codemastr, cptr == server1.blah. The reason is as follows:

I am the sender of the command, therefore, regardless of the server, I am sptr. However, on server1 I am also the "client" that send the command to server1 (server1 received the command directly from me). However, in the case of server2, cptr is server1.blah because server1 is the actual "client" that sent the message to server2. sptr is the original sender, cptr is the client that directly sent the message to me.
Another routines that confused me while trying to understand the code is exit_client() which takes THREE aClient pointers. Some code passed the same pointer to all three, some pass NULL to one and something to the others, some modules pass a cptr and an acptr, etc.
Exit client is a bit more tricky. The reason for this is that exit_client can disconnect both users and servers. When servers disconnect, things become more complicated (disconnecting a server means you also need to disconnect all the clients connected to that server). Because of this, more information needs to be passed.
-- codemastr

aquanight
Official supporter
Posts: 862
Joined: Tue Mar 09, 2004 10:47 pm
Location: Boise, ID

Post by aquanight » Sat Apr 02, 2005 2:19 am

Heh, I think I remember having to explain sptr vs. cptr before. It's fun :P .

w00t
Posts: 1136
Joined: Thu Mar 25, 2004 3:31 am
Location: Nowra, Australia

Post by w00t » Sun Apr 03, 2005 5:20 am

I always get confused when I'm trying to write server->server stuff :P so I end up checking cptr, when I meant sptr... which means it screws itself when I have s1->s2->s3, but is ok for s1->s2 :/ VERY fun.
-ChatSpike IRC Network [http://www.chatspike.net]
-Denora Stats [http://denora.nomadirc.net]
-Omerta [http://www.barafranca.com]

Post Reply