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