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