+GLOBAL const char *
+Client_TypeText(CLIENT *Client)
+{
+ assert(Client != NULL);
+ switch (Client_Type(Client)) {
+ case CLIENT_USER:
+ return "User";
+ break;
+ case CLIENT_SERVICE:
+ return "Service";
+ break;
+ case CLIENT_SERVER:
+ return "Server";
+ break;
+ default:
+ return "Client";
+ }
+} /* Client_TypeText */
+
+
+/**
+ * Destroy user or service client.
+ */
+static void
+Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool SendQuit)
+{
+ if(Client->conn_id != NONE) {
+ /* Local (directly connected) client */
+ Log(LOG_NOTICE,
+ "%s \"%s\" unregistered (connection %d): %s.",
+ Client_TypeText(Client), Client_Mask(Client),
+ Client->conn_id, Txt);
+ Log_ServerNotice('c', "Client exiting: %s (%s@%s) [%s]",
+ Client_ID(Client), Client_User(Client),
+ Client_Hostname(Client), Txt);
+
+ if (SendQuit) {
+ /* Inforam all the other servers */
+ if (FwdMsg)
+ IRC_WriteStrServersPrefix(NULL,
+ Client, "QUIT :%s", FwdMsg );
+ else
+ IRC_WriteStrServersPrefix(NULL,
+ Client, "QUIT :");
+ }
+ } else {
+ /* Remote client */
+ LogDebug("%s \"%s\" unregistered: %s.",
+ Client_TypeText(Client), Client_Mask(Client), Txt);
+
+ if(SendQuit) {
+ /* Inform all the other servers, but the ones in the
+ * direction we got the QUIT from */
+ if(FwdMsg)
+ IRC_WriteStrServersPrefix(Client_NextHop(Client),
+ Client, "QUIT :%s", FwdMsg );
+ else
+ IRC_WriteStrServersPrefix(Client_NextHop(Client),
+ Client, "QUIT :" );
+ }
+ }
+
+ /* Unregister client from channels */
+ Channel_Quit(Client, FwdMsg ? FwdMsg : Client->id);
+
+ /* Register client in My_Whowas structure */
+ Client_RegisterWhowas(Client);
+} /* Destroy_UserOrService */
+
+
+/**
+ * Introduce a new user or service client to a remote server.
+ *
+ * @param To The remote server to inform.
+ * @param Prefix Prefix for the generated commands.
+ * @param data CLIENT structure of the new client.
+ */
+static void
+cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
+{
+ CLIENT *c = (CLIENT *)data;
+
+ (void)Client_Announce(To, Prefix, c);
+
+} /* cb_introduceClient */
+
+
+/**
+ * Announce an user or service to a server.
+ *
+ * This function differentiates between RFC1459 and RFC2813 server links and
+ * generates the appropriate commands to register the user or service.
+ *
+ * @param Client Server
+ * @param Prefix Prefix for the generated commands
+ * @param User User to announce
+ */
+GLOBAL bool
+Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User)
+{
+ CONN_ID conn;
+ char *modes, *user, *host;
+
+ modes = Client_Modes(User);
+ user = Client_User(User) ? Client_User(User) : "-";
+ host = Client_Hostname(User) ? Client_Hostname(User) : "-";
+
+ conn = Client_Conn(Client);
+ if (Conn_Options(conn) & CONN_RFC1459) {
+ /* RFC 1459 mode: separate NICK and USER commands */
+ if (! Conn_WriteStr(conn, "NICK %s :%d",
+ Client_ID(User), Client_Hops(User) + 1))
+ return DISCONNECTED;
+ if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
+ Client_ID(User), user, host,
+ Client_ID(Client_Introducer(User)),
+ Client_Info(User)))
+ return DISCONNECTED;
+ if (modes[0]) {
+ if (! Conn_WriteStr(conn, ":%s MODE %s +%s",
+ Client_ID(User), Client_ID(User),
+ modes))
+ return DISCONNECTED;
+ }
+ } else {
+ /* RFC 2813 mode: one combined NICK or SERVICE command */
+ if (Client_Type(User) == CLIENT_SERVICE
+ && Client_HasFlag(Client, 'S')) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "SERVICE %s %d * +%s %d :%s",
+ Client_Mask(User),
+ Client_MyToken(Client_Introducer(User)),
+ modes, Client_Hops(User) + 1,
+ Client_Info(User)))
+ return DISCONNECTED;
+ } else {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "NICK %s %d %s %s %d +%s :%s",
+ Client_ID(User), Client_Hops(User) + 1,
+ user, host,
+ Client_MyToken(Client_Introducer(User)),
+ modes, Client_Info(User)))
+ return DISCONNECTED;
+ }
+ }
+
+ if (Client_HasFlag(Client, 'M')) {
+ /* Synchronize metadata */
+ if (Client_HostnameCloaked(User)) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s cloakhost :%s",
+ Client_ID(User),
+ Client_HostnameCloaked(User)))
+ return DISCONNECTED;
+ }
+
+ if (Client_AccountName(User)) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s accountname :%s",
+ Client_ID(User),
+ Client_AccountName(User)))
+ return DISCONNECTED;
+ }
+
+ if (Conn_GetCertFp(Client_Conn(User))) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s certfp :%s",
+ Client_ID(User),
+ Conn_GetCertFp(Client_Conn(User))))
+ return DISCONNECTED;
+ }
+ }
+
+ return CONNECTED;
+} /* Client_Announce */
+
+
+#ifdef DEBUG
+
+GLOBAL void
+Client_DebugDump(void)
+{
+ CLIENT *c;
+
+ Log(LOG_DEBUG, "Client status:");
+ c = My_Clients;
+ while (c) {
+ Log(LOG_DEBUG,
+ " - %s: type=%d, host=%s, user=%s, conn=%d, start=%ld, flags=%s",
+ Client_ID(c), Client_Type(c), Client_Hostname(c),
+ Client_User(c), Client_Conn(c), Client_StartTime(c),
+ Client_Flags(c));
+ c = (CLIENT *)c->next;
+ }
+} /* Client_DumpClients */
+
+#endif
+
+