1 /**
2  * Copyright 2010 Christian Liesch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * @file
19  *
20  * @Author christian liesch <liesch@gmx.ch>
21  *
22  * Implementation of the HTTP Test Tool math module
23  */
24 
25 /************************************************************************
26  * Includes
27  ***********************************************************************/
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <math.h>
33 
34 #include "eval.h"
35 
36 #include "module.h"
37 
38 /************************************************************************
39  * Definitions
40  ***********************************************************************/
41 
42 /************************************************************************
43  * Globals
44  ***********************************************************************/
45 
46 /************************************************************************
47  * Local
48  ***********************************************************************/
49 
50 /************************************************************************
51  * Commands
52  ***********************************************************************/
53 /**
54  * Evaluate a math expression, should be extended with >, <, >=,<=, == and !
55  * @param worker IN worker instance
56  * @param parent IN caller
57  * @param ptmp IN temporary pool for this function
58  * @return APR_SUCCESS or APR_EINVAL if expression is incorrect
59  */
block_MATH_EVAL(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)60 static apr_status_t block_MATH_EVAL(worker_t *worker, worker_t *parent,
61                                     apr_pool_t *ptmp) {
62   apr_status_t status;
63   long val;
64   const char *value = store_get(worker->params, "1");
65   const char *var = store_get(worker->params, "2");
66   char *expr = apr_pstrdup(ptmp, value);
67   math_eval_t *eval_hook = math_eval_make(ptmp);
68 
69   if (!value) {
70     worker_log(worker, LOG_ERR, "Missing expression");
71     return APR_EINVAL;
72   }
73 
74   if (!var) {
75     worker_log(worker, LOG_ERR, "Missing variable");
76     return APR_EINVAL;
77   }
78 
79   if ((status = math_evaluate(eval_hook, expr, &val)) != APR_SUCCESS) {
80     worker_log(worker, LOG_ERR, "Expression \"%s\" not valid", expr);
81     return status;
82   }
83 
84   worker_var_set(parent, var, apr_ltoa(ptmp, val));
85   return APR_SUCCESS;
86 }
87 
88 /**
89  * Legacy simple math evaluator us block_MATH_EVAL instead
90  * @param worker IN worker instance
91  * @param parent IN caller
92  * @param ptmp IN temporary pool for this function
93  * @return APR_SUCCESS or APR_EINVAL if expression is incorrect
94  */
block_MATH_OP(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)95 static apr_status_t block_MATH_OP(worker_t *worker, worker_t *parent,
96                                   apr_pool_t *ptmp) {
97   const char *param;
98   const char *op;
99   apr_int64_t ileft;
100   apr_int64_t iright;
101   apr_int64_t result;
102 
103   param = store_get(worker->params, "1");
104   if (param == NULL) {
105     worker_log(worker, LOG_ERR, "<left> value expected");
106     return APR_EINVAL;
107   }
108   ileft = apr_atoi64(param);
109 
110   op = store_get(worker->params, "2");
111   if (op == NULL) {
112     worker_log(worker, LOG_ERR, "ADD, SUB, MUL or DIV expected");
113     return APR_EINVAL;
114   }
115 
116   param = store_get(worker->params, "3");
117   if (param == NULL) {
118     worker_log(worker, LOG_ERR, "<right> value expected");
119     return APR_EINVAL;
120   }
121   iright = apr_atoi64(param);
122 
123   param = store_get(worker->params, "4");
124   if (param == NULL) {
125     worker_log(worker, LOG_ERR, "<var> expected");
126     return APR_EINVAL;
127   }
128 
129   /* do operation */
130   if (strcasecmp(op, "ADD") == 0) {
131     result = ileft + iright;
132   }
133   else if (strcasecmp(op, "SUB") == 0) {
134     result = ileft - iright;
135   }
136   else if (strcasecmp(op, "MUL") == 0) {
137     result = ileft * iright;
138   }
139   else if (strcasecmp(op, "DIV") == 0) {
140     if (iright == 0) {
141       worker_log(worker, LOG_ERR, "Division by zero");
142       return APR_EINVAL;
143     }
144     result = ileft / iright;
145   }
146   else {
147     worker_log(worker, LOG_ERR, "Unknown operant %s", op);
148     return APR_ENOTIMPL;
149   }
150 
151   /* store it do var */
152   worker_var_set(parent, param, apr_off_t_toa(ptmp, result));
153 
154   return APR_SUCCESS;
155 }
156 
157 /**
158  * Generate a random number.
159  * @param worker IN worker instance
160  * @param parent IN caller
161  * @param ptmp IN temporary pool for this function
162  * @return APR_SUCCESS or APR_EINVAL if expression is incorrect
163  */
block_MATH_RAND(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)164 static apr_status_t block_MATH_RAND(worker_t *worker, worker_t *parent,
165                                     apr_pool_t *ptmp) {
166   int start;
167   int end;
168   int result;
169 
170   const char *val = store_get(worker->params, "1");
171   if (val == NULL) {
172     worker_log(worker, LOG_ERR, "No start defined");
173     return APR_EINVAL;
174   }
175   start = apr_atoi64(val);
176 
177   val = store_get(worker->params, "2");
178   if (val == NULL) {
179     worker_log(worker, LOG_ERR, "No end defined");
180     return APR_EINVAL;
181   }
182   end = apr_atoi64(val);
183 
184   val = store_get(worker->params, "3");
185   if (val == NULL) {
186     worker_log(worker, LOG_ERR, "No variable name specified");
187     return APR_EINVAL;
188   }
189 
190   result = start + (rand() % (end - start));
191 
192   worker_var_set(parent, val, apr_itoa(ptmp, result));
193 
194   return APR_SUCCESS;
195 }
196 
197 /************************************************************************
198  * Module
199  ***********************************************************************/
math_module_init(global_t * global)200 apr_status_t math_module_init(global_t * global) {
201   apr_status_t status;
202   if ((status =
203        module_command_new(global, "MATH", "_EVAL", "<expression> <var>",
204                           "callculates <expression> and stores it in <var>",
205                           block_MATH_EVAL)) != APR_SUCCESS) {
206     return status;
207   }
208 
209   if ((status =
210        module_command_new(global, "MATH", "_OP", "<left> ADD|SUB|DIV|MUL <right> <variable>",
211                           "Legacy math evaluator use _MATH:EVAL instead",
212                           block_MATH_OP)) != APR_SUCCESS) {
213     return status;
214   }
215 
216   if ((status =
217        module_command_new(global, "MATH", "_RAND", "<start> <end> <var>",
218                           "Generates a number between <start> and <end> and stores result in"
219 			  "<var>",
220                           block_MATH_RAND)) != APR_SUCCESS) {
221     return status;
222   }
223   return APR_SUCCESS;
224 }
225