1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Tiny Embedded JavaScript parser
19 *
20 * Copyright 2003, ASUSTeK Inc.
21 * All Rights Reserved.
22 *
23 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of ASUSTeK Inc.;
24 * the contents of this file may not be disclosed to third parties, copied
25 * or duplicated in any form, in whole or in part, without the prior
26 * written permission of ASUSTeK Inc..
27 *
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <string.h>
34#include <ctype.h>
35
36#include <httpd.h>
37#include <bcmnvram.h>
38#include <rtconfig.h>
39
40static char * get_arg(char *args, char **next);
41static void call(char *func, FILE *stream);
42
43/* Look for unquoted character within a string */
44static char *
45unqstrstr(char *haystack, char *needle)
46{
47	char *cur;
48	int q;
49
50	for (cur = haystack, q = 0;
51	     cur < &haystack[strlen(haystack)] && !(!q && !strncmp(needle, cur, strlen(needle)));
52	     cur++) {
53		if (*cur == '"')
54			q ? q-- : q++;
55	}
56	return (cur < &haystack[strlen(haystack)]) ? cur : NULL;
57}
58
59static char *
60get_arg(char *args, char **next)
61{
62	char *arg, *end;
63
64	/* Parse out arg, ... */
65	if (!(end = unqstrstr(args, ","))) {
66		end = args + strlen(args);
67		*next = NULL;
68	} else
69		*next = end + 1;
70
71	/* Skip whitespace and quotation marks on either end of arg */
72	for (arg = args; isspace((int)*arg) || *arg == '"'; arg++);
73	for (*end-- = '\0'; isspace((int)*end) || *end == '"'; end--)
74		*end = '\0';
75
76	return arg;
77}
78
79static void
80call(char *func, FILE *stream)
81{
82	char *args, *end, *next;
83	int argc;
84	char * argv[16];
85	struct ej_handler *handler;
86
87	/* Parse out ( args ) */
88	if (!(args = strchr(func, '(')))
89		return;
90	if (!(end = unqstrstr(func, ")")))
91		return;
92	*args++ = *end = '\0';
93
94	/* Set up argv list */
95	for (argc = 0; argc < 16 && args && *args; argc++, args = next) {
96		if (!(argv[argc] = get_arg(args, &next)))
97			break;
98	}
99
100	/* Call handler */
101	for (handler = &ej_handlers[0]; handler->pattern; handler++) {
102//		if (strncmp(handler->pattern, func, strlen(handler->pattern)) == 0)
103		if (strcmp(handler->pattern, func) == 0)
104			handler->output(0, stream, argc, argv);
105	}
106}
107#ifdef TRANSLATE_ON_FLY
108static const char *asp_mark1 = "<%", *asp_mark2 = "%>", *kw_mark1 = "<#", *kw_mark2 = "#>";
109
110// Call this function if and only if we can read whole <%....%> pattern.
111static char *
112process_asp (char *s, char *e, FILE *f)
113{
114	char *func = NULL, *end = NULL;
115
116	if (s == NULL || e == NULL || f == NULL || s >= e) {
117		return NULL;
118	}
119
120	for (func = s; func < e; func = end) {
121		/* Skip initial whitespace */
122		for (; isspace((int)*func); func++);
123		if (!(end = unqstrstr(func, ";")))
124			break;
125		*end++ = '\0';
126
127		/* Call function */
128		call(func, f);
129
130		// skip asp_mark2
131		end = e + strlen (asp_mark2);
132		break;
133	}
134
135	return end;
136}
137
138// Call this function if and only if we can read whole <#....#> pattern.
139static char *
140translate_lang (char *s, char *e, FILE *f, kw_t *pkw)
141{
142	char *end = NULL, *name = NULL, *desc = NULL;
143	if (s == NULL || e == NULL || f == NULL || pkw == NULL || s >= e) {
144		return NULL;
145	}
146
147	for (name = s; name < e; name = end) {
148		/* Skip initial whitespace */
149		for (; isspace((int)*name); name++);
150		if (!(end = strstr(name, kw_mark2)))
151			break;
152		*end++ = '=';	// '#' --> '=', search_desc() need '='
153		*end++ = '\0';	// '>' --> '\0'
154
155		desc = search_desc (pkw, name);
156		if (desc != NULL) {
157#ifdef RTCONFIG_ODMPID
158			static char pattern1[2048];
159			char *p_PID_STR = NULL;
160			char *PID_STR = nvram_safe_get("productid");
161			char *ODM_PID_STR = nvram_safe_get("odmpid");
162			char *pSrc, *pDest;
163			int pid_len, odm_len;
164
165			pid_len = strlen(PID_STR);
166			odm_len = strlen(ODM_PID_STR);
167
168			if (odm_len && strcmp(PID_STR, ODM_PID_STR) != 0) {
169				pSrc  = desc;
170				pDest = pattern1;
171				while((p_PID_STR = strstr(pSrc, PID_STR)))
172				{
173					memcpy(pDest, pSrc, p_PID_STR - pSrc);
174					pDest += (p_PID_STR - pSrc);
175					pSrc   =  p_PID_STR + pid_len;
176
177					memcpy(pDest, ODM_PID_STR, odm_len);
178					pDest += odm_len;
179				}
180				if(pDest != pattern1)
181				{
182					strcpy(pDest, pSrc);
183					desc = pattern1;
184				}
185			}
186#endif
187			fprintf (f, "%s", desc);
188		}
189
190		// skip kw_mark2
191		end = e + strlen (kw_mark2);
192		break;
193	}
194
195	return end;
196}
197
198#ifdef TRANSLATE_ON_FLY
199extern char Accept_Language[];
200extern int is_firsttime(void);
201#endif
202
203// This translation engine can not process <%...%> interlace with <#...#>
204void
205do_ej(char *path, FILE *stream)
206{
207#define PATTERN_LENGTH	1024
208#define FRAG_SIZE	128
209#define RESERVE_SIZE	4
210	int frag_size = FRAG_SIZE;
211	int pattern_size = PATTERN_LENGTH - RESERVE_SIZE;
212	char pat_buf[PATTERN_LENGTH];
213	char *pattern = pat_buf, *asp = NULL, *asp_end = NULL, *key = NULL, *key_end = NULL;
214	char *start_pat, *end_pat, *lang;
215	FILE *fp;
216	int conn_break = 0;
217	size_t ret, read_len, len;
218	int no_translate = 1;
219	static kw_t kw = {0, 0, NULL, NULL};
220
221	if (!(fp = fopen(path, "r")))
222		return;
223
224#ifdef TRANSLATE_ON_FLY
225	// Load dictionary file
226
227	// If the router is restored to default, using browser's language setting to display ALL pages
228	if (is_firsttime () && Accept_Language[0] != '\0' && nvram_match("ui_Setting", "0")) {
229		lang = Accept_Language;
230		nvram_set("ui_Setting" , "1");
231	} else {
232		lang = nvram_safe_get("preferred_lang");
233		if (!check_lang_support(lang)) {
234			nvram_set("preferred_lang", "EN");
235			lang = "EN";
236		}
237	}
238
239	if(!strncmp(nvram_safe_get("territory_code"), "JP", 2) && strcmp(nvram_safe_get("ATEMODE"), "1")){
240		nvram_set("preferred_lang", "JP");
241		lang = "JP";
242	}
243
244	if (load_dictionary (lang, &kw))	{
245		no_translate = 0;
246	}
247#endif  //defined TRANSLATE_ON_FLY
248
249	start_pat = end_pat = pattern;
250	memset (pattern + pattern_size, 0, 4);
251	while (conn_break == 0)
252	{
253		int special;
254
255		// Arrange pattern[] if available buffer length (end_pat~pattern[pattern_size]) is smaller than frag_size
256		if (((pattern + pattern_size) - end_pat) < frag_size)
257		{
258			len = end_pat - start_pat;
259			memcpy (pattern, start_pat, len);
260			start_pat = pattern;
261			end_pat = start_pat + len;
262			*end_pat = '\0';
263		}
264
265		read_len = (pattern + pattern_size) - end_pat;
266		len = fread (end_pat, 1, read_len, fp);
267		if (len == 0)   {
268			if (start_pat < end_pat)	{
269				fwrite (start_pat, 1, (size_t) (end_pat - start_pat), stream);
270			}
271			break;
272		}
273		end_pat += len;
274		*end_pat = '\0';
275
276		asp = strstr (start_pat, asp_mark1);
277		key = NULL;
278		if (no_translate == 0)  {
279			key = strstr (start_pat, kw_mark1);
280		}
281		special = 0;
282		while ((start_pat < end_pat) && special == 0)
283		{
284			int postproc = 0;	/* 0: need more data; 1: translate; 2: execute asp; 3: write only; */
285			char *s, *e, *p;
286
287			/*				 asp      asp_end
288			 *				 ^	^
289			 *      +------------------------------<%.......%>-------------------------------+
290			 *  |	 XXXXXXXXXXXXXXXXXXXXX<#.......#>YYYYYYYYYYYYYYYYYY0	    |0000
291			 *  +------------------------------------------------------------------------+
292			 *  ^	 ^		    ^	^ ^		 ^	     ^
293			 *  |	 |		    |	| p		 |	     |
294			 *  pattern   start_pat,s	  key,e(2) key_end	     end_pat,e(1)  pattern + pattern_size
295			 *				     ^				|
296			 *				     +--------------------------------+
297			 *
298			 */
299
300			// If <%...%> and <#...#> do not exist in pattern[], write whole pattern[].
301			s = start_pat;
302			e = end_pat;
303
304			if (key != NULL && asp == NULL) {
305				e = key;						// Write start_pat ~ (key - 1)
306				key_end = strstr (key, kw_mark2);
307				if (key_end != NULL) {	// We do have <#...#> in pattern[].
308					postproc = 1;
309				}
310			} else if (key != NULL && asp != NULL)  {
311				// We have <%...%> and <#...#> in pattern[], process first occurrence
312				if (asp < key)  {
313					e = asp;					// Write start_pat ~ (asp - 1)
314					asp_end = strstr (asp, asp_mark2);
315					if (asp_end != NULL) {	// We do have whole <%...%>.
316						postproc = 2;
317					}
318				} else {
319					e = key;					// Write start_pat ~ (key - 1)
320					key_end = strstr (key, kw_mark2);
321					if (key_end != NULL) {	// We do have whole <#...#>.
322						postproc = 1;
323					}
324				}
325			} else if (key == NULL && asp != NULL)  {
326				e = asp;						// Write start_pat ~ (asp - 1)
327				asp_end = strstr (asp, asp_mark2);
328				if (asp_end != NULL) {	// We do have whole <%...%>.
329					postproc = 2;
330				}
331			} else {
332				// Special case. If last character is '<'
333				// DO NOT write this character due to next one may be % or #.
334				if (*(e-1) == *asp_mark1 || *(e-1) == *kw_mark1)	{
335					special = 1;
336					e--;
337				}
338
339				postproc = 3;
340			}
341
342			// process text preceeding <# or <%
343			if (e > s) {
344				ret = fwrite (s, 1, (size_t) (e - s), stream);
345				if (ret == 0 || ret < (e - s))  {
346					/* the connection had been damaged. DO NOT process another data. */
347					/* (reduce response time of httpd) */
348//					cprintf ("fwrite() ret %d, s %p e %p len %d, break do_ej()'s while loop\n", ret, s, e, e-s);
349					conn_break = 1;
350					break;
351				} else {
352					start_pat = e;
353				}
354			}
355			// post process
356			p = NULL;
357			if (postproc == 1) {				// translate
358				p = translate_lang (key + strlen (kw_mark1), key_end, stream, &kw);
359				if (no_translate == 0 && p != NULL) {
360					key = strstr (p, kw_mark1);
361				}
362			} else if (postproc == 2) {			// execute asp
363				p = process_asp (asp + strlen (asp_mark1), asp_end, stream);
364				if (p != NULL)  {
365					asp = strstr (p, asp_mark1);
366				}
367			} else if (postproc == 3) {			// no <%...%> or <#...#>
368				p = e;
369			} else if (postproc == 0) {			// read more data
370				break;
371			}
372
373			if (p != NULL)  {
374				start_pat = p;
375			}
376
377		}	/* while ((start_pat < end_pat) && special == 0) */
378	}		/* while (conn_break == 0) */
379
380	fflush (stream);
381	fclose(fp);
382
383	if (pattern != pat_buf) {
384		free (pattern);
385	}
386}
387#endif  // defined TRANSLATE_ON_FLY
388
389int
390ejArgs(int argc, char **argv, char *fmt, ...)
391{
392	va_list	ap;
393	int arg;
394	char *c;
395
396	if (!argv)
397		return 0;
398
399	va_start(ap, fmt);
400	for (arg = 0, c = fmt; c && *c && arg < argc;) {
401		if (*c++ != '%')
402			continue;
403		switch (*c) {
404		case 'd':
405			*(va_arg(ap, int *)) = atoi(argv[arg]);
406			break;
407		case 's':
408			*(va_arg(ap, char **)) = argv[arg];
409			break;
410		}
411		arg++;
412	}
413	va_end(ap);
414
415	return arg;
416}
417