#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"
{ "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." },
{ "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" },
struct chanData *channel;
enum levelOption lvlOpt;
enum charOption chOpt;
+ int i;
channel = calloc(1, sizeof(struct chanData));
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;
{
struct mod_chanmode change;
char msgbuf[MAXLEN];
+ int i;
/* After channel unregistration, the following must be cleaned
up:
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;
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
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);
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;
}
}
+ 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))
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;
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);