fix possible crash on user deletion
[srvx.git] / src / helpfile.c
index 38a61ec6877d03c8e511e4bba9ec2f26a9a4f632..aaf0d80ab961eed5048225f3d241f536ff4a106b 100644 (file)
@@ -23,6 +23,7 @@
 #include "log.h"
 #include "modcmd.h"
 #include "nickserv.h"
+#include "spamserv.h"
 
 #if defined(HAVE_DIRENT_H)
 #include <dirent.h>
@@ -38,10 +39,10 @@ static const struct message_entry msgtab[] = {
     { NULL, NULL }
 };
 
-#define DEFAULT_LINE_SIZE      MAX_LINE_SIZE
+#define DEFAULT_LINE_SIZE       MAX_LINE_SIZE
 #define DEFAULT_TABLE_SIZE      80
 
-extern struct userNode *global, *chanserv, *opserv, *nickserv;
+extern struct userNode *global, *chanserv, *opserv, *nickserv, *spamserv;
 struct userNode *message_dest;
 struct userNode *message_source;
 struct language *lang_C;
@@ -205,7 +206,7 @@ static struct language *language_read(const char *name)
     /* Read all the translations from the directory. */
     while ((dirent = readdir(dir))) {
         snprintf(filename, sizeof(filename), "languages/%s/%s", name, dirent->d_name);
-        if (!strcmp(dirent->d_name,"parent")) {
+        if (!strcmp(dirent->d_name, "parent")) {
             continue;
         } else if (!strcmp(dirent->d_name, "strings.db")) {
             dict = parse_database(filename);
@@ -235,6 +236,9 @@ static void language_read_list(void)
         if (dirent->d_name[0] == '.')
             continue;
         snprintf(namebuf, sizeof(namebuf), "languages/%s", dirent->d_name);
+        if (!strcmp(dirent->d_name, "strings.db")) {
+            continue;
+        }
         if (stat(namebuf, &sbuf) < 0) {
             log_module(MAIN_LOG, LOG_INFO, "Skipping language entry '%s' (unable to stat).", dirent->d_name);
             continue;
@@ -243,7 +247,8 @@ static void language_read_list(void)
             log_module(MAIN_LOG, LOG_INFO, "Skipping language entry '%s' (not directory).", dirent->d_name);
             continue;
         }
-        language_alloc(dirent->d_name);
+        if (!dict_find(languages, dirent->d_name, NULL))
+            language_alloc(dirent->d_name);
     }
     closedir(dir);
 }
@@ -389,7 +394,7 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
     void (*irc_send)(struct userNode *from, const char *to, const char *msg);
     static struct string_buffer input;
     unsigned int size, ipos, pos, length, chars_sent, use_color;
-    unsigned int expand_pos, expand_ipos, newline_ipos;
+    unsigned int expand_ipos, newline_ipos;
     char line[MAX_LINE_SIZE];
 
     if (IsChannelName(dest) || *dest == '$') {
@@ -415,8 +420,8 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
 
     /* figure out how to send the messages */
     if (handle) {
-       msg_type |= (HANDLE_FLAGGED(handle, USE_PRIVMSG) ? 1 : 0);
-       use_color = HANDLE_FLAGGED(handle, MIRC_COLOR);
+        msg_type |= (HANDLE_FLAGGED(handle, USE_PRIVMSG) ? 1 : 0);
+        use_color = HANDLE_FLAGGED(handle, MIRC_COLOR);
         size = handle->screen_width;
         if (size > sizeof(line))
             size = sizeof(line);
@@ -443,23 +448,23 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
      * that requires a very big intermediate buffer.
      */
     expand_ipos = newline_ipos = ipos = 0;
-    expand_pos = pos = 0;
+    pos = 0;
     chars_sent = 0;
     while (input.list[ipos]) {
-       char ch, *value, *free_value;
+        char ch, *value, *free_value;
 
         while ((ch = input.list[ipos]) && (ch != '$') && (ch != '\n') && (pos < size)) {
-           line[pos++] = ch;
+            line[pos++] = ch;
             ipos++;
-       }
+        }
 
-       if (!input.list[ipos])
+        if (!input.list[ipos])
             goto send_line;
         if (input.list[ipos] == '\n') {
             ipos++;
             goto send_line;
         }
-       if (pos == size) {
+        if (pos == size) {
             unsigned int new_ipos;
             /* Scan backwards for a space in the input, until we hit
              * either the last newline or the last variable expansion.
@@ -484,53 +489,65 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
             ipos = new_ipos;
             while (input.list[ipos] == ' ')
                 ipos++;
-           goto send_line;
-       }
+            goto send_line;
+        }
 
         free_value = 0;
-       switch (input.list[++ipos]) {
+        switch (input.list[++ipos]) {
         /* Literal '$' or end of string. */
-       case 0:
-           ipos--;
-       case '$':
-           value = "$";
-           break;
-       /* The following two expand to mIRC color codes if enabled
-          by the user. */
-       case 'b':
-           value = use_color ? "\002" : "";
-           break;
-       case 'o':
-           value = use_color ? "\017" : "";
-           break;
+        case 0:
+            ipos--;
+        case '$':
+            value = "$";
+            break;
+        /* The following two expand to mIRC color codes if enabled
+           by the user. */
+        case 'b':
+            value = use_color ? "\002" : "";
+            break;
+        case 'o':
+            value = use_color ? "\017" : "";
+            break;
         case 'r':
             value = use_color ? "\026" : "";
             break;
-       case 'u':
-           value = use_color ? "\037" : "";
-           break;
-       /* Service nicks. */
+        case 'u':
+            value = use_color ? "\037" : "";
+            break;
+        /* Service nicks. */
         case 'S':
             value = src->nick;
             break;
-       case 'G':
-           value = global ? global->nick : "Global";
-           break;
-       case 'C':
-           value = chanserv ? chanserv->nick : "ChanServ";
-           break;
-       case 'O':
-           value = opserv ? opserv->nick : "OpServ";
-           break;
-       case 'N':
+        case 'G':
+            value = global ? global->nick : "Global";
+            break;
+        case 'C':
+            value = chanserv ? chanserv->nick : "ChanServ";
+            break;
+        case 'O':
+            value = opserv ? opserv->nick : "OpServ";
+            break;
+        case 'N':
             value = nickserv ? nickserv->nick : "NickServ";
             break;
+        case 'X':
+            value = spamserv ? spamserv->nick : "SpamServ";
+            break;
         case 's':
             value = self->name;
             break;
-       case 'H':
-           value = handle ? handle->handle : "Account";
-           break;
+        case 'A':
+            value = handle ? handle->handle : "Account";
+            break;
+        case 'U':
+            value = message_dest ? message_dest->nick : "Nick";
+            break;
+        case 'I':
+            value = message_dest ? (IsFakeIdent(message_dest) ? message_dest->fakeident : message_dest->ident) : "Ident";
+            break;
+        case 'H':
+            value = message_dest ? (IsFakeHost(message_dest) ? message_dest->fakehost : message_dest->hostname) : "Hostname";
+            break;
 #define SEND_LINE(TRUNCED) do { \
     line[pos] = 0; \
     if (pos > 0) { \
@@ -543,9 +560,9 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
     newline_ipos = ipos; \
     if (!(msg_type & MSG_TYPE_MULTILINE)) return chars_sent; \
 } while (0)
-       /* Custom expansion handled by helpfile-specific function. */
-       case '{':
-       case '(': {
+        /* Custom expansion handled by helpfile-specific function. */
+        case '{':
+        case '(': {
             struct helpfile_expansion exp;
             char *name_end = input.list + ipos + 1, *colon = NULL;
 
@@ -585,20 +602,20 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
                 break;
             default:
                 value = "";
-                log_module(MAIN_LOG, LOG_ERROR, "Invalid exp.type %d from expansion function %p.", exp.type, expand_f);
+                log_module(MAIN_LOG, LOG_ERROR, "Invalid exp.type %d from expansion function %p.", exp.type, (void*)expand_f);
                 break;
             }
             ipos = name_end - input.list;
             break;
         }
-       default:
+        default:
         fallthrough:
             value = alloca(3);
             value[0] = '$';
             value[1] = input.list[ipos];
             value[2] = 0;
-       }
-       ipos++;
+        }
+        ipos++;
         while ((pos + strlen(value) > size) || strchr(value, '\n')) {
             unsigned int avail;
             avail = size - pos - 1;
@@ -635,17 +652,15 @@ vsend_message(const char *dest, struct userNode *src, struct handle_info *handle
             }
         }
         length = strlen(value);
-       memcpy(line + pos, value, length);
+        memcpy(line + pos, value, length);
         if (free_value)
             free(free_value);
-       pos += length;
+        pos += length;
         if ((pos < size-1) && input.list[ipos]) {
-            expand_pos = pos;
             expand_ipos = ipos;
             continue;
         }
       send_line:
-        expand_pos = pos;
         expand_ipos = ipos;
         SEND_LINE(0);
 #undef SEND_LINE
@@ -659,7 +674,7 @@ send_message(struct userNode *dest, struct userNode *src, const char *format, ..
     int res;
     va_list ap;
 
-    if (IsLocal(dest)) return 0;
+    if (IsLocal(dest) && !IsDummy(dest)) return 0;
     va_start(ap, format);
     res = vsend_message(dest->nick, src, dest->handle_info, 0, NULL, format, ap);
     va_end(ap);
@@ -671,7 +686,7 @@ send_message_type(int msg_type, struct userNode *dest, struct userNode *src, con
     int res;
     va_list ap;
 
-    if (IsLocal(dest)) return 0;
+    if (IsLocal(dest) && !IsDummy(dest)) return 0;
     va_start(ap, format);
     res = vsend_message(dest->nick, src, dest->handle_info, msg_type, NULL, format, ap);
     va_end(ap);
@@ -729,7 +744,7 @@ send_help(struct userNode *dest, struct userNode *src, struct helpfile *hf, cons
     if (!rec)
         return send_message(dest, src, "MSG_TOPIC_UNKNOWN");
     if (rec->type != RECDB_QSTRING)
-       return send_message(dest, src, "HFMSG_HELP_NOT_STRING");
+        return send_message(dest, src, "HFMSG_HELP_NOT_STRING");
     return _send_help(dest, src, hf->expand, rec->d.qstring);
 }
 
@@ -933,25 +948,25 @@ unlistify_help(const char *key, void *data, void *extra)
 
     switch (rd->type) {
     case RECDB_QSTRING:
-       dict_insert(newdb, strdup(key), alloc_record_data_qstring(GET_RECORD_QSTRING(rd)));
-       return 0;
+        dict_insert(newdb, strdup(key), alloc_record_data_qstring(GET_RECORD_QSTRING(rd)));
+        return 0;
     case RECDB_STRING_LIST: {
-       struct string_list *slist = GET_RECORD_STRING_LIST(rd);
-       char *dest;
-       unsigned int totlen, len, i;
-
-       for (i=totlen=0; i<slist->used; i++)
-           totlen = totlen + strlen(slist->list[i]) + 1;
-       dest = alloca(totlen+1);
-       for (i=totlen=0; i<slist->used; i++) {
-           len = strlen(slist->list[i]);
-           memcpy(dest+totlen, slist->list[i], len);
-           dest[totlen+len] = '\n';
-           totlen = totlen + len + 1;
-       }
-       dest[totlen] = 0;
-       dict_insert(newdb, strdup(key), alloc_record_data_qstring(dest));
-       return 0;
+        struct string_list *slist = GET_RECORD_STRING_LIST(rd);
+        char *dest;
+        unsigned int totlen, len, i;
+
+        for (i=totlen=0; i<slist->used; i++)
+            totlen = totlen + strlen(slist->list[i]) + 1;
+        dest = alloca(totlen+1);
+        for (i=totlen=0; i<slist->used; i++) {
+            len = strlen(slist->list[i]);
+            memcpy(dest+totlen, slist->list[i], len);
+            dest[totlen+len] = '\n';
+            totlen = totlen + len + 1;
+        }
+        dest[totlen] = 0;
+        dict_insert(newdb, strdup(key), alloc_record_data_qstring(dest));
+        return 0;
     }
     case RECDB_OBJECT: {
         dict_iterator_t it;
@@ -981,7 +996,7 @@ unlistify_help(const char *key, void *data, void *extra)
         return 0;
     }
     default:
-       return 1;
+        return 1;
     }
 }
 
@@ -1002,8 +1017,8 @@ open_helpfile(const char *fname, expand_func_t expand)
         dict_insert(language_find("C")->helpfiles, hf->name, hf);
     }
     if (db) {
-       dict_foreach(db, unlistify_help, hf->db);
-       free_database(db);
+        dict_foreach(db, unlistify_help, hf->db);
+        free_database(db);
     }
     return hf;
 }
@@ -1033,10 +1048,27 @@ void helpfile_init(void)
     language_read_list();
 }
 
-void helpfile_finalize(void)
+static void helpfile_read_languages(void)
 {
     dict_iterator_t it;
+    dict_t dict;
+
+    language_read_list();
     for (it = dict_first(languages); it; it = iter_next(it))
         language_read(iter_key(it));
+
+    /* If the user has a strings.db in their languages directory,
+     * allow that to override C language strings.
+     */
+    dict = parse_database("languages/strings.db");
+    if (dict) {
+        language_set_messages(lang_C, dict);
+        free_database(dict);
+    }
+}
+
+void helpfile_finalize(void)
+{
+    conf_register_reload(helpfile_read_languages);
     reg_exit_func(language_cleanup);
 }