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