8c31bb29d5f258dc57a21ae35e6c097895f54f3d
[NeonServV5.git] / src / cmd_neonserv_extscript.c
1 /* cmd_neonserv_extscript.c - NeonServ v5.2
2  * Copyright (C) 2011  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "cmd_neonserv.h"
19 #include <fcntl.h>
20
21 /*
22 * argv[0]      'toys' if it's a toy command (check if toys are enabled)
23 * argv[0-*]    script name & parameter patterns
24 * argv[argc-1] all arguments passed to the command
25 */
26
27 static TIMEQ_CALLBACK(neonserv_cmd_extscript_callback);
28
29 struct neonserv_cmd_extscript_cache {
30     struct ClientSocket *client, *textclient;
31     struct UserNode *user;
32     struct ChanNode *chan;
33     int answere_channel;
34     FILE *pipe;
35 };
36
37 CMD_BIND(neonserv_cmd_extscript) {
38     int i, j;
39     char *args[MAXNUMPARAMS];
40     int argpos = 0;
41     char *next, *curr;
42     char command[1024];
43     int commandpos = 0;
44     char part[MAXLEN];
45     int partpos;
46     int escape_param;
47     int answere_channel = 0;
48     //check first arg
49     if(argc && !stricmp(argv[0], "toys")) {
50         if(!chan) return; //toys are not allowed via query
51         MYSQL_RES *res;
52         MYSQL_ROW row;
53         printf_mysql_query("SELECT `channel_toys` FROM `channels` WHERE `channel_name` = '%s'", escape_string(chan->name));
54         res = mysql_use();
55         row = mysql_fetch_row(res);
56         if(!row || !strcmp(row[0], "0")) {
57             //disabled
58             reply(getTextBot(), user, "NS_FUN_DISABLED", chan->name);
59             return;
60         } else if(!strcmp(row[0], "2"))
61             answere_channel = 1;
62         argc--;
63         argv++;
64     }
65     //parse arguments
66     if(argc < 2) return;
67     curr = argv[argc-1];
68     while(curr) {
69         next = strstr(curr, " ");
70         args[argpos++] = curr;
71         if(next) {
72             *next = '\0';
73             curr = next+1;
74         } else
75             curr = NULL;
76     }
77     //parse command pattern and build command
78     commandpos = sprintf(command, "%s", argv[0]);
79     for(i = 1; i < argc-1; i++) {
80         partpos = 0;
81         escape_param = 1;
82         if(argv[i][0] == '$') {
83             argv[i]++;
84             if(argv[i][strlen(argv[i])-1] == '-') {
85                 argv[i][strlen(argv[i])-1] = '\0';
86                 j = atoi(argv[i]);
87                 if(j <= argpos)
88                     partpos = sprintf(part, "%s", merge_argv(args, j-1, argpos));
89             } else if((j = atoi(argv[i])) > 0) {
90                 if(j <= argpos)
91                     partpos = sprintf(part, "%s", args[j-1]);
92             } else if(!strcmp(argv[i], "c")) {
93                 partpos = sprintf(part, "%s", (chan ? chan->name : ""));
94             } else if(!strcmp(argv[i], "n")) {
95                 partpos = sprintf(part, "%s", user->nick);
96             } else if(!strcmp(argv[i], "a")) {
97                 partpos = sprintf(part, "%s", ((user->flags & USERFLAG_ISAUTHED) ? user->auth : ""));
98             } else if(!strcmp(argv[i], "access")) {
99                 if(chan)
100                     partpos = sprintf(part, "%d", getChannelAccess(user, chan));
101             }
102         } else {
103             partpos = sprintf(part, "%s", argv[i]);
104             escape_param = 0;
105         }
106         //escape shell argument
107         command[commandpos++] = ' ';
108         if(escape_param) {
109             command[commandpos++] = '\'';
110             for(j = 0; j < partpos; j++) {
111                 if(part[j] == '\'') {
112                     command[commandpos++] = '\'';
113                     command[commandpos++] = '\\';
114                     command[commandpos++] = '\'';
115                     command[commandpos++] = '\'';
116                 } else
117                     command[commandpos++] = part[j];
118             }
119             command[commandpos++] = '\'';
120         } else
121             commandpos += sprintf(command + commandpos, " %s", part);
122     }
123     command[commandpos] = '\0';
124     //we should now have a valid command
125     
126     struct neonserv_cmd_extscript_cache *cache = malloc(sizeof(*cache));
127     if (!cache) {
128         perror("malloc() failed");
129         return;
130     }
131     cache->client = client;
132     cache->textclient = getTextBot();
133     cache->user = user;
134     cache->chan = chan;
135     cache->answere_channel = answere_channel;
136     cache->pipe = popen(command, "r");
137     #ifndef WIN32
138     fcntl(fileno(cache->pipe), F_SETFL, O_NONBLOCK);
139     #endif
140     timeq_add(1, neonserv_cmd_extscript_callback, cache);
141 }
142
143 static TIMEQ_CALLBACK(neonserv_cmd_extscript_callback) {
144     struct neonserv_cmd_extscript_cache *cache = data;
145     char command[512];
146     char *a;
147     if(feof(cache->pipe)) {
148         pclose(cache->pipe);
149         free(cache);
150         return;
151     }
152     while (fgets(command, 512, cache->pipe) != NULL) {
153         if((a = strchr(command, '\n'))) 
154             *a = '\0';
155         if(cache->answere_channel)
156             putsock(cache->client, "PRIVMSG %s :%s", cache->chan->name, command);
157         else
158             reply(cache->textclient, cache->user, "%s", command);
159     }
160     timeq_add(1, neonserv_cmd_extscript_callback, cache);
161 }
162
163
164