about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ngircd/conf.c60
-rw-r--r--src/ngircd/conf.h3
-rw-r--r--src/ngircd/defines.h3
-rw-r--r--src/ngircd/irc.c108
4 files changed, 146 insertions, 28 deletions
diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c
index bea4d619..d5a28bd7 100644
--- a/src/ngircd/conf.c
+++ b/src/ngircd/conf.c
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -54,6 +54,7 @@ static CONF_SERVER New_Server;
 static int New_Server_Idx;
 
 static char Conf_MotdFile[FNAME_LEN];
+static char Conf_HelpFile[FNAME_LEN];
 
 static void Set_Defaults PARAMS(( bool InitServers ));
 static bool Read_Config PARAMS(( bool TestOnly, bool IsStarting ));
@@ -316,6 +317,7 @@ Conf_Test( void )
 	printf("  AdminInfo1 = %s\n", Conf_ServerAdmin1);
 	printf("  AdminInfo2 = %s\n", Conf_ServerAdmin2);
 	printf("  AdminEMail = %s\n", Conf_ServerAdminMail);
+	printf("  HelpFile = %s\n", Conf_HelpFile);
 	printf("  Info = %s\n", Conf_ServerInfo);
 	printf("  Listen = %s\n", Conf_ListenAddress);
 	if (Using_MotdFile) {
@@ -701,8 +703,11 @@ Set_Defaults(bool InitServers)
 	Conf_ListenAddress = NULL;
 	array_free(&Conf_ListenPorts);
 	array_free(&Conf_Motd);
+	array_free(&Conf_Helptext);
 	strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
 	strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
+	strlcpy(Conf_HelpFile, DOCDIR, sizeof(Conf_HelpFile));
+	strlcat(Conf_HelpFile, HELP_FILE, sizeof(Conf_HelpFile));
 	strcpy(Conf_ServerPwd, "");
 	strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
 	Conf_UID = Conf_GID = 0;
@@ -784,39 +789,44 @@ no_listenports(void)
 }
 
 /**
- * Read MOTD ("message of the day") file.
+ * Read contents of a text file into an array.
+ *
+ * This function is used to read the MOTD and help text file, for exampe.
  *
  * @param filename	Name of the file to read.
+ * @return		true, when the file has been read in.
  */
-static void
-Read_Motd(const char *filename)
+static bool
+Read_TextFile(const char *Filename, const char *Name, array *Destination)
 {
 	char line[127];
 	FILE *fp;
+	int line_no = 1;
 
-	if (*filename == '\0')
-		return;
+	if (*Filename == '\0')
+		return false;
 
-	fp = fopen(filename, "r");
+	fp = fopen(Filename, "r");
 	if (!fp) {
-		Config_Error(LOG_WARNING, "Can't read MOTD file \"%s\": %s",
-					filename, strerror(errno));
-		return;
+		Config_Error(LOG_WARNING, "Can't read %s file \"%s\": %s",
+					Name, Filename, strerror(errno));
+		return false;
 	}
 
-	array_free(&Conf_Motd);
-	Using_MotdFile = true;
-
+	array_free(Destination);
 	while (fgets(line, (int)sizeof line, fp)) {
-		ngt_TrimLastChr( line, '\n');
+		ngt_TrimLastChr(line, '\n');
 
 		/* add text including \0 */
-		if (!array_catb(&Conf_Motd, line, strlen(line) + 1)) {
-			Log(LOG_WARNING, "Cannot add MOTD text: %s", strerror(errno));
+		if (!array_catb(Destination, line, strlen(line) + 1)) {
+			Log(LOG_WARNING, "Cannot read/add \"%s\", line %d: %s",
+			    Filename, line_no, strerror(errno));
 			break;
 		}
+		line_no++;
 	}
 	fclose(fp);
+	return true;
 }
 
 /**
@@ -1037,8 +1047,16 @@ Read_Config(bool TestOnly, bool IsStarting)
 	}
 
 	/* No MOTD phrase configured? (re)try motd file. */
-	if (array_bytes(&Conf_Motd) == 0)
-		Read_Motd(Conf_MotdFile);
+	if (array_bytes(&Conf_Motd) == 0) {
+		if (Read_TextFile(Conf_MotdFile, "MOTD", &Conf_Motd))
+			Using_MotdFile = true;
+	}
+
+	/* Try to read ngIRCd help text file. */
+	(void)Read_TextFile(Conf_HelpFile, "help text", &Conf_Helptext);
+	if (!array_bytes(&Conf_Helptext))
+		Config_Error(LOG_WARNING,
+		    "No help text available, HELP command will be of limited use.");
 
 #ifdef SSL_SUPPORT
 	/* Make sure that all SSL-related files are readable */
@@ -1305,6 +1323,12 @@ Handle_GLOBAL( int Line, char *Var, char *Arg )
 			Config_Error_TooLong(Line, Var);
 		return;
 	}
+	if (strcasecmp(Var, "HelpFile") == 0) {
+		len = strlcpy(Conf_HelpFile, Arg, sizeof(Conf_HelpFile));
+		if (len >= sizeof(Conf_HelpFile))
+			Config_Error_TooLong(Line, Var);
+		return;
+	}
 	if (strcasecmp(Var, "Listen") == 0) {
 		if (Conf_ListenAddress) {
 			Config_Error(LOG_ERR,
diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h
index f85a25fa..ac42746c 100644
--- a/src/ngircd/conf.h
+++ b/src/ngircd/conf.h
@@ -111,6 +111,9 @@ GLOBAL char Conf_ServerAdminMail[CLIENT_INFO_LEN];
 /** Message of the day (MOTD) of this server */
 GLOBAL array Conf_Motd;
 
+/** Help text of this server */
+GLOBAL array Conf_Helptext;
+
 /** Array of ports this server should listen on */
 GLOBAL array Conf_ListenPorts;
 
diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h
index ad9716fd..3850b581 100644
--- a/src/ngircd/defines.h
+++ b/src/ngircd/defines.h
@@ -77,6 +77,9 @@
 /** Name of the MOTD file. */
 #define MOTD_FILE "/ngircd.motd"
 
+/** Name of the help file. */
+#define HELP_FILE "/Commands.txt"
+
 /** Default chroot() directory. */
 #define CHROOT_DIR ""
 
diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c
index a678f904..e76abcb8 100644
--- a/src/ngircd/irc.c
+++ b/src/ngircd/irc.c
@@ -44,6 +44,7 @@ static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
 static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
 				      char *targetMask, char *message,
 				      bool SendErrors));
+static bool Help PARAMS((CLIENT *Client, const char *Topic));
 
 
 /**
@@ -315,29 +316,116 @@ IRC_TRACE( CLIENT *Client, REQUEST *Req )
 } /* IRC_TRACE */
 
 
+/**
+ * Handler for the IRC "HELP" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
-IRC_HELP( CLIENT *Client, REQUEST *Req )
+IRC_HELP(CLIENT *Client, REQUEST *Req)
 {
 	COMMAND *cmd;
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 	/* Bad number of arguments? */
-	if( Req->argc > 0 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc > 1)
+		return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
+					  Client_ID(Client), Req->command);
 
-	cmd = Parse_GetCommandStruct( );
-	while( cmd->name )
-	{
-		if( ! IRC_WriteStrClient( Client, "NOTICE %s :%s", Client_ID( Client ), cmd->name )) return DISCONNECTED;
+	IRC_SetPenalty(Client, 2);
+
+	if ((Req->argc == 0 && array_bytes(&Conf_Helptext) > 0)
+	    || (Req->argc >= 1 && strcasecmp(Req->argv[0], "Commands") != 0)) {
+		/* Help text available and requested */
+		if (Req->argc >= 1)
+			return Help(Client, Req->argv[0]);
+
+		if (!Help(Client, "Intro"))
+			return DISCONNECTED;
+		return CONNECTED;
+	}
+
+	cmd = Parse_GetCommandStruct();
+	while(cmd->name) {
+		if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
+					Client_ID(Client), cmd->name))
+			return DISCONNECTED;
 		cmd++;
 	}
-	
-	IRC_SetPenalty( Client, 2 );
 	return CONNECTED;
 } /* IRC_HELP */
 
 
+/**
+ * Send help for a given topic to the client.
+ *
+ * @param Client The client requesting help.
+ * @param Topoc The help topic requested.
+ * @return CONNECTED or DISCONNECTED.
+ */
+static bool
+Help(CLIENT *Client, const char *Topic)
+{
+	char *line;
+	size_t helptext_len, len_str, idx_start, lines = 0;
+	bool in_article = false;
+
+	assert(Client != NULL);
+	assert(Topic != NULL);
+
+	helptext_len = array_bytes(&Conf_Helptext);
+	line = array_start(&Conf_Helptext);
+	while (helptext_len > 0) {
+		len_str = strlen(line) + 1;
+		assert(helptext_len >= len_str);
+		helptext_len -= len_str;
+
+		if (in_article) {
+			/* The first character in each article text line must
+			 * be a TAB (ASCII 9) character which will be stripped
+			 * in the output. If it is not a TAB, the end of the
+			 * article has been reached. */
+			if (line[0] != '\t') {
+				if (lines > 0)
+					return CONNECTED;
+				else
+					break;
+			}
+
+			/* A single '.' character indicates an empty line */
+			if (line[1] == '.' && line[2] == '\0')
+				idx_start = 2;
+			else
+				idx_start = 1;
+
+			if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
+						Client_ID(Client),
+						&line[idx_start]))
+				return DISCONNECTED;
+			lines++;
+
+		} else {
+			if (line[0] == '-' && line[1] == ' '
+			    && strcasecmp(&line[2], Topic) == 0)
+				in_article = true;
+		}
+
+		line += len_str;
+	}
+
+	/* Help topic not found (or empty)! */
+	if (!IRC_WriteStrClient(Client, "NOTICE %s :No help for \"%s\" found!",
+				Client_ID(Client), Topic))
+		return DISCONNECTED;
+
+	return CONNECTED;
+}
+
+
 static char *
 #ifdef ZLIB
 Option_String(CONN_ID Idx)