added cmd_kick, cmd_kickban & all the functions depending on
authorpk910 <philipp@zoelle1.de>
Sat, 27 Aug 2011 14:34:53 +0000 (16:34 +0200)
committerpk910 <philipp@zoelle1.de>
Sat, 27 Aug 2011 14:45:16 +0000 (16:45 +0200)
14 files changed:
ChanNode.c
ChanNode.h
ChanUser.c
IRCParser.c
IRCParser.h
UserNode.c
UserNode.h
bot_NeonServ.c
cmd_neonserv_deop.c
cmd_neonserv_deopall.c
cmd_neonserv_kick.c [new file with mode: 0644]
cmd_neonserv_kickban.c [new file with mode: 0644]
tools.c
tools.h

index 14ab084d43378595a7014712b653b8c80c739157..e00acb601866ee6428046b2671fb640c321b7a81 100644 (file)
@@ -156,6 +156,7 @@ struct ChanNode* addChannel(const char *name) {
     }
     strcpy(chan->name, name);
     chan->user = NULL;
+    chan->usercount = 0;
     chan->chanbot = NULL;
     chan->topic[0] = 0;
     chan->flags = 0;
index c93625c3b1072b997d8a36d4fb875f841d2c785e..8b8808ca9963e5a5783fdf73a0693a10231e2bc5 100644 (file)
@@ -13,6 +13,7 @@ struct ChanNode {
     char name[CHANNELLEN+1];
     char topic[TOPICLEN+1];
     struct ChanUser *user;
+    unsigned int usercount;
     unsigned char flags;
     unsigned int modes;
     char **mode_str_args;
index 9c98d465955247f8b65995e859cdee10d7b34721..69fc006621744b3d91061f134c6560490e8c8817 100644 (file)
@@ -16,6 +16,7 @@ struct ChanUser* addChanUser(struct ChanNode *chan, struct UserNode *user) {
 
     chanuser->next_user = chan->user;
     chan->user = chanuser;
+    chan->usercount++;
 
     chanuser->next_chan = user->channel;
     user->channel = chanuser;
@@ -74,6 +75,7 @@ void delChanUser(struct ChanUser *chanuser, int freeChanUser) {
     last = NULL;
     for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) {
         if(cchanuser == chanuser) {
+            chanuser->chan->usercount--;
             if(last) 
                 last->next_user = chanuser->next_user;
             else
@@ -98,6 +100,7 @@ void removeChanUserFromLists(struct ChanUser *chanuser, int remove_from_userlist
         last = NULL;
         for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) {
             if(cchanuser == chanuser) {
+                chanuser->chan->usercount--;
                 if(last) 
                     last->next_user = chanuser->next_user;
                 else
index ccfc67af8fb9f1ffbd9821e898c6250a8f32e59e..12fca5735ead1c072909da2c4c3d0ea5e2861df0 100644 (file)
@@ -390,3 +390,14 @@ void reply(struct ClientSocket *client, struct UserNode *user, const char *text,
     write_socket(client, sendBuf, pos+1);
 }
 
+char* merge_argv(char **argv, int start, int end) {
+    int i;
+    char *p = NULL;
+    for(i = start; i < end; i++) {
+        p = argv[i];
+        while(*p) p++;
+        *p = ' ';
+    }
+    if(p) *p = '\0';
+    return argv[start];
+}
index 5d216873bedf6540d61af7ba0844b25c911c32a5..d0ab8d6762e68830ef7712d323266bc31512b36a 100644 (file)
@@ -20,5 +20,6 @@ void bot_disconnect(struct ClientSocket *client);
 void init_parser();
 void free_parser();
 void reply(struct ClientSocket *client, struct UserNode *user, const char *text, ...);
+char* merge_argv(char **argv, int start, int end);
 
 #endif
\ No newline at end of file
index 5b6c9c11259c79019d5e7d7a562f656be786ab33..e857cb7f46785bf4597f6d97e876751f7986e1b7 100644 (file)
@@ -101,6 +101,19 @@ struct UserNode* searchUserByNick(const char *nick) { //case insensitive
     return NULL;
 }
 
+int countUsersWithHost(char *host) {
+    int i, count = 0;
+    struct UserNode *user;
+    for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN+1; i++) {
+        for(user = userList[i]; user; user = user->next) {
+            if(!strcmp(user->host, host)) {
+                count++;
+            }
+        }
+    }
+    return count;
+}
+
 struct UserNode* addUser(const char *nick) {
     int userListIndex = get_nicklist_entry(*nick);
     if(userListIndex == -1 || !is_valid_nick(nick))
index 6347ef51c9e13c0ad76a875c8131c6147339a7c0..b98c06334922c21f479c16fd16365662aa1ba8d8 100644 (file)
@@ -36,6 +36,7 @@ void free_UserNode();
 int is_valid_nick(const char *nick);
 struct UserNode* getUserByNick(const char *nick);
 struct UserNode* getUserByMask(const char *mask);
+int countUsersWithHost(char *host);
 struct UserNode* searchUserByNick(const char *nick);
 struct UserNode* addUser(const char *nick);
 struct UserNode* addUserMask(const char *mask);
index 53d8bbe3ebbc51853bc21691f30bc056ebd5633e..ad5d544c13f7ff4bb455989e3235b75df5e00b79 100644 (file)
@@ -97,6 +97,11 @@ static const struct default_language_entry msgtab[] = {
     {"NS_VOICEALL_DONE", "Voiced \002%d\002 users in %s."},
     {"NS_DEOPALL_DONE", "Deopped \002%d\002 users in %s."},
     {"NS_DEVOICEALL_DONE", "Devoiced \002%d\002 users in %s."},
+    {"NS_KICK_DONE", "Kicked \002%d\002 users from %s"},
+    {"NS_KICK_FAIL", "\002%s\002 could not kick some of the nicks you provided."},
+    {"NS_KICKBAN_DONE", "KickBanned \002%d\002 users from %s"},
+    {"NS_KICKBAN_FAIL", "\002%s\002 could not kickban some of the nicks you provided."},
+    {"NS_LAME_MASK", "\002%s\002 is a little too general. Try making it more specific."},
     {NULL, NULL}
 };
 
@@ -127,8 +132,8 @@ INCLUDE ALL CMD's HERE
 #include "cmd_neonserv_devoice.c"
 #include "cmd_neonserv_devoiceall.c"
 //#include "cmd_neonserv_uset.c"
-//#include "cmd_neonserv_kick.c"
-//#include "cmd_neonserv_kickban.c"
+#include "cmd_neonserv_kick.c"
+#include "cmd_neonserv_kickban.c"
 //#include "cmd_neonserv_ban.c"
 //#include "cmd_neonserv_unban.c"
 //#include "cmd_neonserv_unbanall.c"
@@ -274,6 +279,8 @@ void init_NeonServ() {
     register_command(BOTID, "voiceall",     neonserv_cmd_voiceall,  0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH,  "#channel_canvoice",    0);
     register_command(BOTID, "devoiceall",   neonserv_cmd_devoiceall, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_canvoice",    0);
     register_command(BOTID, "set",          neonserv_cmd_set,       0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH,  "#channel_setters",     0);
+    register_command(BOTID, "kick",         neonserv_cmd_kick,      0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH,  "#channel_cankick",     0);
+    register_command(BOTID, "kickban",      neonserv_cmd_kickban,   0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH,  "#channel_cankick,#channel_canban", 0);
     
     start_bots();
     bind_bot_ready(neonserv_bot_ready);
index 993a0114a3577fe397f7f0da816166e69787cf78..65b42fc4e09d0ae0d97c919966630457a9e1ce55 100644 (file)
@@ -36,7 +36,7 @@ static CMD_BIND(neonserv_cmd_deop) {
 }
 
 static USERLIST_CALLBACK(neonserv_cmd_deop_userlist_lookup) {
-    struct neonserv_cmd_kick_cache *cache = data;
+    struct neonserv_cmd_deop_cache *cache = data;
     neonserv_cmd_deop_async1(cache->client, cache->textclient, cache->user, chan, cache->argv, cache->argc);
     int i;
     for(i = 0; i < cache->argc; i++) {
index 1f1f390c11a84169dae3460b0cb25037061a62f5..c6f044c7d009c5c92e9ce919c8f578657d4ef11e 100644 (file)
@@ -36,7 +36,7 @@ static CMD_BIND(neonserv_cmd_deopall) {
 }
 
 static USERLIST_CALLBACK(neonserv_cmd_deopall_userlist_lookup) {
-    struct neonserv_cmd_kick_cache *cache = data;
+    struct neonserv_cmd_deopall_cache *cache = data;
     neonserv_cmd_deopall_async1(cache->client, cache->textclient, cache->user, chan, cache->argv, cache->argc);
     int i;
     for(i = 0; i < cache->argc; i++) {
diff --git a/cmd_neonserv_kick.c b/cmd_neonserv_kick.c
new file mode 100644 (file)
index 0000000..4ff4466
--- /dev/null
@@ -0,0 +1,131 @@
+
+/*
+* argv[0]    nick[,*auth[,*!*@mask[...]]]
+* argv[1-*]  reason
+*/
+static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup);
+static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason);
+
+struct neonserv_cmd_kick_cache {
+    struct ClientSocket *client, *textclient;
+    struct UserNode *user;
+    char *nicks;
+    char *reason;
+};
+
+static CMD_BIND(neonserv_cmd_kick) {
+    struct neonserv_cmd_kick_cache *cache = malloc(sizeof(*cache));
+    if (!cache) {
+        perror("malloc() failed");
+        return;
+    }
+    cache->client = client;
+    cache->textclient = getTextBot();
+    cache->user = user;
+    cache->nicks = strdup(argv[0]);
+    if(argc > 1) {
+        cache->reason = strdup(merge_argv(argv, 1, argc));
+    } else
+        cache->reason = NULL;
+    get_userlist(chan, neonserv_cmd_kick_userlist_lookup, cache);
+}
+
+static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup) {
+    struct neonserv_cmd_kick_cache *cache = data;
+    neonserv_cmd_kick_async1(cache->client, cache->textclient, cache->user, chan, cache->nicks, (cache->reason ? cache->reason : "Bye."));
+    free(cache->nicks);
+    if(cache->reason)
+        free(cache->reason);
+    free(cache);
+}
+
+static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason) {
+    int i, kicked_users = 0, provided_nicks = 0;
+    char *nick, *nextnick;
+    struct UserNode *cuser;
+    struct ChanUser *chanuser;
+    nextnick = nicks;
+    check_mysql();
+    while((nick = nextnick)) {
+        nextnick = strstr(nick, ",");
+        if(nextnick) {
+            *nextnick = '\0';
+            nextnick++;
+        }
+        if(!*nick) continue;
+        if(is_ircmask(nick)) {
+            //KICK HOSTMASK
+            char usermask[NICKLEN+USERLEN+HOSTLEN+3];
+            struct ChanUser *kick_chanuser[chan->usercount];
+            int kick_chanuser_pos = 0;
+            for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) {
+                cuser = chanuser->user;
+                sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host);
+                if(!match(nick, usermask)) {
+                    provided_nicks++;
+                    if(isNetworkService(chanuser->user)) {
+                        reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
+                        continue;
+                    }
+                    if(isUserProtected(chan, cuser, user)) {
+                        reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+                        continue;
+                    }
+                    kick_chanuser[kick_chanuser_pos++] = chanuser;
+                    if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) {
+                        kick_chanuser_pos = 0;
+                        reply(textclient, user, "NS_LAME_MASK", nick);
+                        break;
+                    }
+                }
+            }
+            for(i = 0; i < kick_chanuser_pos; i++) {
+                kicked_users++;
+                putsock(client, "KICK %s %s :%s", chan->name, kick_chanuser[i]->user->nick, reason);
+            }
+        } else if(*nick == '*') {
+            //KICK AUTH
+            nick++;
+            cuser = NULL;
+            for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) {
+                if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) {
+                    provided_nicks++;
+                    if(isNetworkService(chanuser->user)) {
+                        reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
+                        continue;
+                    }
+                    if(!cuser) {
+                        //check if the user is protected
+                        if(isUserProtected(chan, chanuser->user, user)) {
+                            reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick);
+                            break; //all other users are also protected...
+                        }
+                        cuser = chanuser->user;
+                    }
+                    kicked_users++;
+                    putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason);
+                }
+            }
+        } else {
+            provided_nicks++;
+            cuser = searchUserByNick(nick);
+            if(!cuser) continue;
+            chanuser = getChanUser(cuser, chan);
+            if(!chanuser) continue;
+            if(isNetworkService(cuser)) {
+                reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick);
+                continue;
+            }
+            if(isUserProtected(chan, cuser, user)) {
+                reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+                continue;
+            }
+            kicked_users++;
+            putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason);
+        }
+    }
+    if(kicked_users == provided_nicks)
+        reply(getTextBot(), user, "NS_KICK_DONE", kicked_users, chan->name);
+    else
+        reply(getTextBot(), user, "NS_KICK_FAIL", client->user->nick);
+}
diff --git a/cmd_neonserv_kickban.c b/cmd_neonserv_kickban.c
new file mode 100644 (file)
index 0000000..4ed5ce0
--- /dev/null
@@ -0,0 +1,136 @@
+
+/*
+* argv[0]    nick[,*auth[,*!*@mask[...]]]
+* argv[1-*]  reason
+*/
+static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup);
+static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason);
+
+struct neonserv_cmd_kickban_cache {
+    struct ClientSocket *client, *textclient;
+    struct UserNode *user;
+    char *nicks;
+    char *reason;
+};
+
+static CMD_BIND(neonserv_cmd_kickban) {
+    struct neonserv_cmd_kickban_cache *cache = malloc(sizeof(*cache));
+    if (!cache) {
+        perror("malloc() failed");
+        return;
+    }
+    cache->client = client;
+    cache->textclient = getTextBot();
+    cache->user = user;
+    cache->nicks = strdup(argv[0]);
+    if(argc > 1) {
+        cache->reason = strdup(merge_argv(argv, 1, argc));
+    } else
+        cache->reason = NULL;
+    get_userlist(chan, neonserv_cmd_kickban_userlist_lookup, cache);
+}
+
+static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup) {
+    struct neonserv_cmd_kickban_cache *cache = data;
+    neonserv_cmd_kickban_async1(cache->client, cache->textclient, cache->user, chan, cache->nicks, (cache->reason ? cache->reason : "Bye."));
+    free(cache->nicks);
+    if(cache->reason)
+        free(cache->reason);
+    free(cache);
+}
+
+static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason) {
+    int i, kicked_users = 0, provided_nicks = 0;
+    char *nick, *nextnick;
+    struct UserNode *cuser;
+    struct ChanUser *chanuser;
+    char usermask[NICKLEN+USERLEN+HOSTLEN+3];
+    nextnick = nicks;
+    check_mysql();
+    while((nick = nextnick)) {
+        nextnick = strstr(nick, ",");
+        if(nextnick) {
+            *nextnick = '\0';
+            nextnick++;
+        }
+        if(!*nick) continue;
+        if(is_ircmask(nick)) {
+            //KICK HOSTMASK
+            struct ChanUser *kickban_chanuser[chan->usercount];
+            int kick_chanuser_pos = 0;
+            for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) {
+                cuser = chanuser->user;
+                sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host);
+                if(!match(nick, usermask)) {
+                    provided_nicks++;
+                    if(isNetworkService(chanuser->user)) {
+                        reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
+                        continue;
+                    }
+                    if(isUserProtected(chan, cuser, user)) {
+                        reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+                        continue;
+                    }
+                    kickban_chanuser[kick_chanuser_pos++] = chanuser;
+                    if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) {
+                        kick_chanuser_pos = 0;
+                        reply(textclient, user, "NS_LAME_MASK", nick);
+                        break;
+                    }
+                }
+            }
+            for(i = 0; i < kick_chanuser_pos; i++) {
+                if(i == 0) {
+                    putsock(client, "MODE %s +b %s", chan->name, nick);
+                }
+                kicked_users++;
+                putsock(client, "KICK %s %s :%s", chan->name, kickban_chanuser[i]->user->nick, reason);
+            }
+        } else if(*nick == '*') {
+            //KICK AUTH
+            nick++;
+            cuser = NULL;
+            for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) {
+                if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) {
+                    provided_nicks++;
+                    if(isNetworkService(chanuser->user)) {
+                        reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
+                        continue;
+                    }
+                    if(!cuser) {
+                        //check if the user is protected
+                        if(isUserProtected(chan, chanuser->user, user)) {
+                            reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick);
+                            break; //all other users are also protected...
+                        }
+                        cuser = chanuser->user;
+                    }
+                    kicked_users++;
+                    putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask));
+                    putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason);
+                }
+            }
+        } else {
+            provided_nicks++;
+            cuser = searchUserByNick(nick);
+            if(!cuser) continue;
+            chanuser = getChanUser(cuser, chan);
+            if(!chanuser) continue;
+            if(isNetworkService(cuser)) {
+                reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick);
+                continue;
+            }
+            if(isUserProtected(chan, cuser, user)) {
+                reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+                continue;
+            }
+            kicked_users++;
+            putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask));
+            putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason);
+        }
+    }
+    if(kicked_users == provided_nicks)
+        reply(getTextBot(), user, "NS_KICKBAN_DONE", kicked_users, chan->name);
+    else
+        reply(getTextBot(), user, "NS_KICKBAN_FAIL", client->user->nick);
+}
diff --git a/tools.c b/tools.c
index 274187229bc3584fde6e50e925841a475907ebd0..938bf790247cc9ffa215d5dba234c2d0d524d083 100644 (file)
--- a/tools.c
+++ b/tools.c
@@ -370,6 +370,46 @@ void freeModeBuffer(struct ModeBuffer *modeBuf) {
     free(modeBuf);
 }
 
+int is_ircmask(const char *text) {
+    while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text)))
+        text++;
+    if (*text++ != '!')
+        return 0;
+    while (*text && *text != '@' && !isspace((char)*text))
+        text++;
+    if (*text++ != '@')
+        return 0;
+    while (*text && !isspace((char)*text))
+        text++;
+    return !*text;
+}
+
+char* generate_banmask(struct UserNode *user, char *buffer) {
+    char *userhost = user->host;
+    char *p1, *p2 = userhost;
+    
+    //find the last dot to identify if the hostmask is a hidden host
+    while((p1 = strstr(userhost, "."))) {
+        p2 = p1;
+    }
+    //TLD database: http://www.iana.org/domains/root/db/
+    //the longest TLD i found was 6 chars long (ignoring the stange exotic ones :D)
+    //but we even ignore '.museum' and '.travel' so we can say that the TLD of our mask needs to be less than 4 chars to be a real domain
+    if(strlen(p2+1) > 4) {
+        sprintf(buffer, "*!*@%s", userhost);
+        return buffer;
+    }
+    
+    //check if the hostname has more than 4 connections (trusted host)
+    if(countUsersWithHost(userhost) > 4) {
+        sprintf(buffer, "*!%s@%s", user->ident, userhost);
+        return buffer;
+    } else {
+        sprintf(buffer, "*!*@%s", userhost);
+        return buffer;
+    }
+}
+
 void init_tools() {
     register_default_language_table(msgtab);
 }
diff --git a/tools.h b/tools.h
index d589ade1767642ad3c7e60edbf6eb63de61c93aa..03bdb64399281efbf3f72c57d51849258dab4461 100644 (file)
--- a/tools.h
+++ b/tools.h
@@ -51,10 +51,15 @@ struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *
 #define modeBufferDeop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'o', USER) 
 #define modeBufferVoice(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'v', USER)
 #define modeBufferDevoice(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'v', USER) 
+#define modeBufferBan(MODEBUF,MASK) modeBufferSet(MODEBUF, 1, 'b', MASK) 
 void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param);
 void flushModeBuffer(struct ModeBuffer *modeBuf);
 void freeModeBuffer(struct ModeBuffer *modeBuf);
 
+int is_ircmask(const char *text);
+
+char* generate_banmask(struct UserNode *user, char *buffer); 
+
 void init_tools();
 
 #endif
\ No newline at end of file