added AdvTopic (advanced topic) with multiple delimiters
authorpk910 <philipp@zoelle1.de>
Tue, 14 Feb 2012 21:15:28 +0000 (22:15 +0100)
committerpk910 <philipp@zoelle1.de>
Tue, 14 Feb 2012 22:16:56 +0000 (23:16 +0100)
src/chanserv.c
src/chanserv.h

index 5ca8a01c228863a91359036dab91ee8fe801e642..d50c9147fbe56909b65f776179f763e99bb2ca8d 100644 (file)
 #define KEY_MAX_TIME        "max_time"
 #define KEY_NOTES           "notes"
 #define KEY_TOPIC_MASK      "topic_mask"
+#define KEY_ADVTOPIC_ENTRIES "adv_topic"
 #define KEY_OWNER_TRANSFER  "owner_transfer"
 #define KEY_EXPIRE          "expire"
 
@@ -262,6 +263,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
     { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
     { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
+    { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
 
     { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
     { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
@@ -279,6 +281,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_INVALID_NUMERIC",   "$b%d$b is not a valid choice.  Choose one:" },
     { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
     { "CSMSG_SET_TOPICMASK",     "$bTopicMask   $b %s" },
+    { "CSMSG_SET_ADVTOPIC",      "$bAdvTopic    $b %s" },
     { "CSMSG_SET_GREETING",      "$bGreeting    $b %s" },
     { "CSMSG_SET_USERGREETING",  "$bUserGreeting$b %s" },
     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
@@ -1229,6 +1232,7 @@ register_channel(struct chanNode *cNode, char *registrar)
     struct chanData *channel;
     enum levelOption lvlOpt;
     enum charOption chOpt;
+    int i;
 
     channel = calloc(1, sizeof(struct chanData));
 
@@ -1245,6 +1249,8 @@ register_channel(struct chanNode *cNode, char *registrar)
         channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
         channel->chOpts[chOpt] = charOptions[chOpt].default_value;
+    for(i = 0; i < MAXADVTOPICENTRIES; i++) 
+        channel->advtopic[i] = NULL;
 
     channel->prev = NULL;
     channel->next = channelList;
@@ -1436,6 +1442,7 @@ unregister_channel(struct chanData *channel, const char *reason)
 {
     struct mod_chanmode change;
     char msgbuf[MAXLEN];
+    int i;
 
     /* After channel unregistration, the following must be cleaned
        up:
@@ -1471,6 +1478,11 @@ unregister_channel(struct chanData *channel, const char *reason)
     free(channel->greeting);
     free(channel->user_greeting);
     free(channel->topic_mask);
+    
+    for(i = 0; i < MAXADVTOPICENTRIES; i++) {
+        if(channel->advtopic[i]) 
+            free(channel->advtopic[i]);
+    }
 
     if(channel->prev)
         channel->prev->next = channel->next;
@@ -4323,8 +4335,34 @@ bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic
     struct chanData *cData = channel->channel_info;
     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
         return 0;
-    if(cData->topic_mask)
-        return !match_ircglob(new_topic, cData->topic_mask);
+    if(cData->topic_mask) 
+    {
+        if(cData->flags & CHANNEL_ADVTOPIC) 
+        {
+            //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
+            char topicmask[TOPICLEN];
+            int skipnum, topicpos = 0;
+            char *ptr = cData->topic_mask;
+            for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
+                switch(*ptr) {
+                case '%':
+                    for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
+                    if(skipnum)
+                        topicmask[topicpos++] = '*';
+                    else
+                        topicmask[topicpos++] = *ptr;
+                    break;
+                default:
+                    topicmask[topicpos++] = *ptr;
+                    break;
+                }
+            }
+            topicmask[topicpos] = 0;
+            return !match_ircglob(new_topic, topicmask);
+        }
+        else
+            return !match_ircglob(new_topic, cData->topic_mask);
+    }
     else if(cData->topic)
         return irccasecmp(new_topic, cData->topic);
     else
@@ -4362,30 +4400,96 @@ static CHANSERV_FUNC(cmd_topic)
             char new_topic[TOPICLEN+1], tchar;
             int pos=0, starpos=-1, dpos=0, len;
 
-            while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
+            if(cData->flags & CHANNEL_ADVTOPIC) 
             {
-                switch(tchar)
+                //first check if there is a leading 'modifier id'
+                int advtopic_index = 0;
+                char numbuf[4];
+                int numpos;
+                for(; topic[pos]; pos++) 
                 {
-                case '*':
-                    if(starpos != -1)
-                        goto bad_mask;
-                    len = strlen(topic);
-                    if((dpos + len) > TOPICLEN)
-                        len = TOPICLEN + 1 - dpos;
-                    memcpy(new_topic+dpos, topic, len);
-                    dpos += len;
-                    starpos = pos;
-                    break;
-                case '\\': tchar = topic_mask[pos++]; /* and fall through */
-                default: new_topic[dpos++] = tchar; break;
+                    if(topic[pos] == ' ') 
+                    {
+                        //leading number found, cut off and store value in advtopic_index
+                        topic[pos] = 0;
+                        advtopic_index = atoi(topic) - 1; //no zerobase
+                        topic = &topic[pos+1];
+                        /* If they say "!topic 2 *", unset advtopic id 2. */
+                        if((topic[0] == '*') && (topic[1] == 0))
+                            topic[0] = 0;
+                    }
+                    if(!isdigit(topic[pos]))
+                        break;
+                }
+                if(advtopic_index < 0 || advtopic_index > MAXADVTOPICENTRIES)
+                {
+                    //invalid id!
+                    reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index);
+                    return 0;
+                }
+                if(cData->advtopic[advtopic_index])
+                    free(cData->advtopic[advtopic_index]);
+                cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
+                char *ptr = topic_mask;
+                while(*ptr && (dpos <= TOPICLEN))
+                {
+                    switch(*ptr)
+                    {
+                    case '%':
+                        ptr++;
+                        for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
+                            numbuf[numpos++] = *ptr;
+                        }
+                        numbuf[numpos] = 0;
+                        if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
+                            ptr -= numpos+1;
+                            new_topic[dpos++] = *ptr; //is % again
+                            break;
+                        }
+                        ptr--;
+                        advtopic_index--; //no zero base
+                        if(!cData->advtopic[advtopic_index])
+                            break; //just leave it empty
+                        len = strlen(cData->advtopic[advtopic_index]);
+                        if((dpos + len) > TOPICLEN)
+                            len = TOPICLEN + 1 - dpos;
+                        memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
+                        dpos += len;
+                        break;
+                    case '\\': 
+                        ptr++; /* and fall through */
+                    default:
+                        new_topic[dpos++] = *ptr;
+                        break;
+                    }
+                    ptr++;
+                }
+            } else {
+                while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
+                {
+                    switch(tchar)
+                    {
+                    case '*':
+                        if(starpos != -1)
+                            goto bad_mask;
+                        len = strlen(topic);
+                        if((dpos + len) > TOPICLEN)
+                            len = TOPICLEN + 1 - dpos;
+                        memcpy(new_topic+dpos, topic, len);
+                        dpos += len;
+                        starpos = pos;
+                        break;
+                    case '\\': tchar = topic_mask[pos++]; /* and fall through */
+                    default: new_topic[dpos++] = tchar; break;
+                    }
+                }
+                if((dpos > TOPICLEN) || tchar)
+                {
+                bad_mask:
+                    reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
+                    reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
+                    return 0;
                 }
-            }
-            if((dpos > TOPICLEN) || tchar)
-            {
-            bad_mask:
-                reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
-                reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
-                return 0;
             }
             new_topic[dpos] = 0;
             SetChannelTopic(channel, chanserv, new_topic, 1);
@@ -5799,6 +5903,11 @@ static MODCMD_FUNC(chan_opt_dynlimit)
     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
 }
 
+static MODCMD_FUNC(chan_opt_advtopic)
+{
+    CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
+}
+
 static MODCMD_FUNC(chan_opt_offchannel)
 {
     struct chanData *cData = channel->channel_info;
@@ -8227,6 +8336,16 @@ chanserv_channel_read(const char *key, struct record_data *hir)
         }
     }
 
+    obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
+    for(it = dict_first(obj); it; it = iter_next(it))
+    {
+        struct record_data *rd = iter_data(it);
+        if(rd->type != RECDB_QSTRING) continue;
+        int advtopic_index = atoi(iter_key(it));
+        if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
+        cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
+    }
+
     if(!IsSuspended(cData)
        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
        && (argc = split_line(str, 0, ArrayLength(argv), argv))
@@ -8499,6 +8618,16 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
     high_present = chanserv_write_users(ctx, channel->users);
     chanserv_write_bans(ctx, channel->bans);
 
+    if(channel->flags & CHANNEL_ADVTOPIC) {
+        saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
+        int advtopic_index;
+        for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
+            if(channel->advtopic[advtopic_index])
+                saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
+        }
+        saxdb_end_record(ctx);
+    }
+
     if(dict_size(channel->notes))
     {
         dict_iterator_t it;
@@ -8804,6 +8933,7 @@ init_chanserv(const char *nick)
     DEFINE_CHANNEL_OPTION(ctcpusers);
     DEFINE_CHANNEL_OPTION(ctcpreaction);
     DEFINE_CHANNEL_OPTION(inviteme);
+    DEFINE_CHANNEL_OPTION(advtopic);
     DEFINE_CHANNEL_OPTION(unreviewed);
     modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
index ac2dfdb3f18fffce5e7b2ce20469eb0f248880ad..2aba69cc1277bc3b42703723a5aa8ca4195cf6a5 100644 (file)
@@ -67,6 +67,7 @@ enum charOption {
 #define CHANNEL_PEON_INVITE     0x00000080 /* (1 << 7) - DEPRECATED */
 #define CHANNEL_OFFCHANNEL      0x00000100 /* (1 << 8) */
 #define CHANNEL_UNREVIEWED      0x00000200 /* (1 << 9) */
+#define CHANNEL_ADVTOPIC        0x00000400 /* (1 << 10) */
 /* Flags with values over 0x20000000 or (1 << 29) will not work
  * because chanData.flags is a 30-bit field.
  */
@@ -75,6 +76,8 @@ enum charOption {
 #define IsSuspended(x)  ((x)->flags & CHANNEL_SUSPENDED)
 #define IsOffChannel(x) (((x)->flags & CHANNEL_OFFCHANNEL) && (off_channel > 1))
 
+#define MAXADVTOPICENTRIES 9
+
 struct chanData
 {
     struct chanNode     *channel;
@@ -106,6 +109,8 @@ struct chanData
     unsigned short      lvlOpts[NUM_LEVEL_OPTIONS];
     unsigned char       chOpts[NUM_CHAR_OPTIONS];
 
+    char *advtopic[9];
+
     struct userData     *users;
     struct banData      *bans;
     struct dict         *notes;