1 /*
2 ** Copyright 1998 - 2002 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 
7 #if	HAVE_CONFIG_H
8 #include	"config.h"
9 #endif
10 
11 #include	<stdio.h>
12 #include	<string.h>
13 #include	<stdlib.h>
14 #include	<ctype.h>
15 #include	"http11.h"
16 #include	"../rfc2045/rfc2045charset.h"
17 
18 #if	HAVE_DIRENT_H
19 #include	<dirent.h>
20 #define	NAMLEN(dirent)	strlen(dirent->d_name)
21 #else
22 #define	dirent	direct
23 #define	NAMLEN(dirent)	((dirent)->d_namlen)
24 #if	HAVE_SYS_NDIR_H
25 #include	<sys/ndir.h>
26 #endif
27 #if	HAVE_SYS_DIR_H
28 #include	<sys/dir.h>
29 #endif
30 #if	HAVE_NDIR_H
31 #include	<ndir.h>
32 #endif
33 #endif
34 
35 extern void error(const char *);
36 
enomem()37 static void enomem()
38 {
39 	error("Out of memory.");
40 }
41 
42 static const char defaultlang[] = HTTP11_DEFAULTLANG;
43 
44 /*
45 ** Based upon Accept-Language: header, figure out which directory in
46 ** HTMLLIBDIR matches it.
47 */
48 
http11_open_langfile(const char * libdir,const char * subdir,const char * file)49 FILE *http11_open_langfile(const char *libdir, const char *subdir,
50 		const char *file)
51 {
52 char	*p=malloc(strlen(libdir)+strlen(subdir)+strlen(file)+3);
53 FILE	*fp;
54 
55 	if (!p)	return (0);
56 	strcat(strcat(strcat(strcat(strcpy(p, libdir), "/"), subdir), "/"),
57 		file);
58 
59 	fp=fopen(p, "r");
60 	free(p);
61 	return (fp);
62 }
63 
64 /**************************************************************************/
65 
66 /* Parse Accept-Language: header */
67 
parse_accept_string(const char * acc_lang,char ** languages,double * weights)68 static size_t parse_accept_string(const char *acc_lang, char **languages,
69 			double *weights)
70 {
71 char *p=strdup(acc_lang ? acc_lang:"");
72 size_t	cnt=0;
73 char	*q, *r;
74 int	has_weights=0;
75 double	*save_weights=weights;
76 
77 	if (!p)	enomem();
78 	for (q=p; (q=strtok(q, ", ")) != 0; q=0)
79 	{
80 		if (languages)
81 		{
82 			q=strdup(q);
83 			if (!q)	enomem();
84 			*languages++=q;
85 		}
86 		if (weights)	*weights=1;	/* Until further notice */
87 		for (r=q; *r; r++)
88 			*r=tolower(*r);
89 		if ((r=strchr(q, ';')) != 0)
90 		{
91 			*r++=0;
92 			if (*r == 'q' && r[1] == '=')
93 			{
94 			double weight=atof(r+2);
95 
96 				if (weights)	*weights=weight;
97 				has_weights=1;
98 			}
99 		}
100 		if (weights)	++weights;
101 		++cnt;
102 	}
103 	free(p);
104 	if (!has_weights && weights)
105 	{
106 	size_t	i;
107 	double weight=1;
108 
109 	/*
110 	** Broken HTTP/1.1 clients do not specify quality factors, and expect
111 	** the server to pick the first one on the list
112 	*/
113 		for (i=cnt; i; )
114 		{
115 			--i;
116 			save_weights[i]=weight;
117 			weight = weight + 1;
118 		}
119 	}
120 
121 	return (cnt);
122 }
123 
http11_best_content_language(const char * libdir,const char * acc_lang)124 char *http11_best_content_language(const char *libdir, const char *acc_lang)
125 {
126 size_t	naccept=parse_accept_string(acc_lang, 0, 0);
127 char **languages=malloc(naccept ? sizeof(char *)*naccept:1);
128 double *weights=malloc(naccept ? sizeof(double)*naccept:1);
129 DIR	*p;
130 struct dirent *de;
131 size_t	i;
132 #if 0
133 double	missweight=1;
134 #endif
135 char	*bestlang=0;
136 double	bestweight=0;
137 int	found_nondefault_match=0;
138 
139 	if (!languages || !weights)
140 	{
141 		if (languages)	free(languages);
142 		if (weights)	free(weights);
143 		enomem();
144 	}
145 	(void)parse_accept_string(acc_lang, languages, weights);
146 #if 0
147 	for (i=0; i<naccept; i++)
148 		if (strcmp(languages[i], "*") == 0)	missweight=weights[i];
149 		/* Default weight */
150 #endif
151 	p=opendir(libdir);
152 	while (p && (de=readdir(p)) != 0)
153 	{
154 	FILE	*fp;
155 
156 		if (*de->d_name == '.')	continue;
157 
158 		if ((fp=http11_open_langfile(libdir, de->d_name, "LOCALE"))
159 				!= 0)
160 		{
161 #if 0
162 		double	myweight=missweight;
163 #else
164 		double  myweight=0;
165 #endif
166 
167 			fclose(fp);
168 			for (i=0; i<naccept; i++)
169 				if (strcmp(languages[i], de->d_name) == 0)
170 				{
171 					myweight=weights[i];
172 					break;
173 				}
174 			if (!bestlang || myweight > bestweight)
175 			{
176 				if (bestlang)	free(bestlang);
177 				bestlang=strdup(de->d_name);
178 				if (!bestlang)	enomem();
179 				bestweight=myweight;
180 				if (i < naccept)
181 					found_nondefault_match=1;
182 			}
183 		}
184 	}
185 	if (p)	closedir(p);
186 	if (!bestlang || !found_nondefault_match)
187 	{
188 		if (bestlang)	free(bestlang);
189 		if ((bestlang=malloc(sizeof(defaultlang))) == 0) enomem();
190 		strcpy(bestlang, defaultlang);
191 	}
192 	for (i=0; i<naccept; i++)
193 		free(languages[i]);
194 	free(languages);
195 	free(weights);
196 	return (bestlang);
197 }
198 
get_http11(const char * libdir,const char * subdir,char * buf,const char * file,const char * def)199 static const char *get_http11(const char *libdir, const char *subdir,
200 		char *buf,
201 		const char *file, const char *def)
202 {
203 FILE	*fp=http11_open_langfile(libdir, subdir, file);
204 
205 	if (fp != 0)
206 	{
207 	size_t	n=fread(buf, 1, 79, fp);
208 
209 		if (n <= 0)	n=0;
210 		buf[n]=0;
211 		fclose(fp);
212 		return (strtok(buf, "\r\n"));
213 	}
214 	return (def);
215 }
216 
http11_content_language(const char * libdir,const char * acc_lang)217 const char *http11_content_language(const char *libdir, const char *acc_lang)
218 {
219 static char	buf[80];
220 
221 	return (get_http11(libdir, acc_lang, buf, "LANGUAGE", defaultlang));
222 }
223 
http11_content_locale(const char * libdir,const char * acc_lang)224 const char *http11_content_locale(const char *libdir, const char *acc_lang)
225 {
226 static char buf[80];
227 const char *p=get_http11(libdir, acc_lang, buf, "LOCALE", "C");
228 
229 	return (p);
230 }
231 
http11_content_ispelldict(const char * libdir,const char * acc_lang)232 const char *http11_content_ispelldict(const char *libdir, const char *acc_lang)
233 {
234 static char buf[80];
235 
236 	return (get_http11(libdir, acc_lang, buf, "ISPELLDICT", defaultlang));
237 }
238 
http11_content_charset(const char * libdir,const char * acc_lang)239 const char *http11_content_charset(const char *libdir, const char *acc_lang)
240 {
241 	char buf[80];
242 	static char buf2[80];
243 	size_t naccept;
244 	char **charsets;
245 	double *weights;
246 	const char *p;
247 	size_t i;
248 
249 	strcpy(buf2, get_http11(libdir, acc_lang, buf, "CHARSET",
250 				RFC2045CHARSET));
251 
252 	p=getenv("HTTP_ACCEPT_CHARSET");
253 
254 	if (!p) p="";
255 
256 	naccept=parse_accept_string(p, 0, 0);
257 	charsets=malloc(naccept ? sizeof(char *)*naccept:1);
258 	weights=malloc(naccept ? sizeof(double)*naccept:1);
259 
260 	if (!charsets || !weights)
261 	{
262 		if (charsets)	free(charsets);
263 		if (weights)	free(weights);
264 		enomem();
265 	}
266 
267 	(void)parse_accept_string(p, charsets, weights);
268 	strcpy(buf, buf2);
269 
270 	for (p=strtok(buf, ", \t\r\n"); p != NULL; p=strtok(NULL, ", \t\r\n"))
271 	{
272 		for (i=0; i<naccept; i++)
273 			if (strcmp(charsets[i], p) == 0)
274 			{
275 				strcpy(buf2, charsets[i]);
276 				for (i=0; i<naccept; i++)
277 					free(charsets[i]);
278 				free(charsets);
279 				free(weights);
280 				return buf2;
281 			}
282 	}
283 
284 	p=strtok(buf2, ", \t\r\n");
285 	if (!p)
286 		p=RFC2045CHARSET;
287 	for (i=0; i<naccept; i++)
288 		free(charsets[i]);
289 	free(charsets);
290 	free(weights);
291 	return p;
292 }
293