163180995246d565c070b07f8ff7560cd57c3d31
[profile/ivi/GUPnP.git] / libgupnp / http-headers.c
1 /*
2  * Copyright (C) 2007, 2008 OpenedHand Ltd.
3  *
4  * Author: Jorn Baayen <jorn@openedhand.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include <config.h>
23
24 #include <locale.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gio/gio.h>
28
29 #include "http-headers.h"
30
31 /* Converts @lang from HTTP language tag format into locale format.
32  * Return value: The index of the '-' character. */
33 static int
34 http_language_from_locale (char *lang)
35 {
36         gboolean tolower;
37         int i, dash_index;
38
39         tolower = FALSE;
40         dash_index = -1;
41
42         for (i = 0; lang[i] != '\0'; i++) {
43                 switch (lang[i]) {
44                 case '_':
45                         /* Underscores to dashes */
46                         lang[i] = '-';
47
48                         /* Lowercase country bit */
49                         tolower = TRUE;
50
51                         /* Save dash index */
52                         dash_index = i;
53
54                         break;
55                 case '.':
56                 case '@':
57                         /* Terminate our string here */
58                         lang[i] = '\0';
59
60                         return dash_index;
61                 default:
62                         if (tolower)
63                                 lang[i] = g_ascii_tolower (lang[i]);
64
65                         break;
66                 }
67         }
68
69         return dash_index;
70 }
71
72 /* Converts @lang from locale format into HTTP language tag format.
73  * Return value: The index of the '_' character. */
74 static int
75 locale_from_http_language (char *lang)
76 {
77         gboolean toupper;
78         int i, underscore_index;
79
80         toupper = FALSE;
81         underscore_index = -1;
82
83         for (i = 0; lang[i] != '\0'; i++) {
84                 switch (lang[i]) {
85                 case '-':
86                         /* Dashes to underscores */
87                         lang[i] = '_';
88
89                         /* Uppercase country bit */
90                         toupper = TRUE;
91
92                         /* Save underscore index */
93                         underscore_index = i;
94
95                         break;
96                 case ';':
97                         /* Terminate our string here */
98                         lang[i] = '\0';
99
100                         return underscore_index;
101                 default:
102                         if (toupper)
103                                 lang[i] = g_ascii_toupper (lang[i]);
104
105                         break;
106                 }
107         }
108
109         return underscore_index;
110 }
111
112 /* Sets the Accept-Language on @message with the language taken from the
113  * current locale. */
114 void
115 http_request_set_accept_language (SoupMessage *message)
116 {
117         char *locale, *lang;
118         int dash_index;
119         GString *tmp;
120
121         locale = setlocale (LC_ALL, NULL);
122         if (locale == NULL)
123                 return;
124
125         if (strcmp (locale, "C") == 0)
126                 return;
127
128         lang = g_strdup (locale);
129
130         dash_index = http_language_from_locale (lang);
131
132         tmp = g_string_new (lang);
133         g_string_append (tmp, ";q=1");
134
135         /* Append preference for basic (non-country specific) language version
136          * if applicable */
137         if (dash_index > 0) {
138                 g_string_append (tmp, ", ");
139
140                 lang[dash_index] = '\0';
141                 g_string_append (tmp, lang);
142                 g_string_append (tmp, ";q=0.5");
143         }
144
145         g_free (lang);
146
147         soup_message_headers_append (message->request_headers,
148                                      "Accept-Language",
149                                      tmp->str);
150
151         g_string_free (tmp, TRUE);
152 }
153
154 static double
155 get_quality (const char *val)
156 {
157         val = strstr (val, ";q=");
158         if (!val)
159                 return 1;
160
161         val += strlen (";q=");
162         return atof (val);
163 }
164
165 static int
166 sort_locales_by_quality (const char *a,
167                          const char *b)
168 {
169         const double diff = get_quality (a) - get_quality (b);
170
171         if (diff == 0.0)
172                 return 0;
173         else if (diff > 0)
174                 return -1;
175
176         return 1;
177 }
178
179 /* Parses the Accept-Language header in @message, and returns its values
180  * in an ordered list in UNIX locale format */
181 GList *
182 http_request_get_accept_locales (SoupMessage *message)
183 {
184         const char *header;
185         char **bits;
186         int i;
187         GList *locales;
188
189         header = soup_message_headers_get_one (message->request_headers,
190                                                "Accept-Language");
191         if (header == NULL)
192                 return NULL;
193
194         locales = NULL;
195
196         bits = g_strsplit (header, ",", -1);
197
198         /* Transform to list */
199         for (i = 0; bits[i] != NULL; i++) {
200                 bits[i] = g_strstrip (bits[i]);
201
202                 switch (bits[i][0]) {
203                 case '\0':
204                         /* Empty */
205                 case '*':
206                         /* Wildcard: ignore */
207                         g_free (bits[i]);
208
209                         break;
210                 default:
211                         locale_from_http_language (bits[i]);
212
213                         /* Because bits is sorted in ascending order */
214                         locales = g_list_prepend (locales, bits[i]);
215
216                         break;
217                 }
218         }
219
220         g_free (bits);
221
222         locales = g_list_sort (locales, (GCompareFunc) sort_locales_by_quality);
223
224         return locales;
225 }
226
227 /* Set Accept-Language header according to @locale. */
228 void
229 http_response_set_content_locale (SoupMessage *msg,
230                                   const char  *locale)
231 {
232         char *lang;
233
234         lang = g_strdup (locale);
235         http_language_from_locale (lang);
236
237         soup_message_headers_append (msg->response_headers,
238                                      "Content-Language",
239                                      lang);
240
241         g_free (lang);
242 }
243
244 /* Set Content-Type header guessed from @path, @data and @data_size using
245  * g_content_type_guess(). */
246 void
247 http_response_set_content_type (SoupMessage  *msg,
248                                 const char   *path,
249                                 const guchar *data,
250                                 gsize         data_size)
251 {
252         char *content_type, *mime;
253
254         content_type = g_content_type_guess
255                                 (path,
256                                  data,
257                                  data_size,
258                                  NULL);
259         mime = g_content_type_get_mime_type (content_type);
260         if (mime == NULL)
261                 mime = g_strdup ("application/octet-stream");
262         else if (strcmp (mime, "application/xml") == 0) {
263                 g_free (mime);
264                 mime = g_strdup ("text/xml; charset=\"utf-8\"");
265         }
266
267         soup_message_headers_append (msg->response_headers,
268                                      "Content-Type",
269                                      mime);
270
271         g_free (mime);
272         g_free (content_type);
273 }
274
275 /* Set Content-Encoding header to gzip and append compressed body */
276 void
277 http_response_set_body_gzip (SoupMessage *msg,
278                              const char  *body,
279                              const gsize  length)
280 {
281         GZlibCompressor *compressor;
282         gboolean finished = FALSE;
283         gsize converted = 0;
284
285         soup_message_headers_append (msg->response_headers,
286                                      "Content-Encoding", "gzip");
287
288         compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1);
289
290         while (! finished) {
291                 GError *error = NULL;
292                 char buf[65536];
293                 gsize bytes_read = 0;
294                 gsize bytes_written = 0;
295
296                 switch (g_converter_convert (G_CONVERTER (compressor),
297                                              body + converted,
298                                              length - converted,
299                                              buf, sizeof (buf),
300                                              G_CONVERTER_INPUT_AT_END,
301                                              &bytes_read, &bytes_written,
302                                              &error)) {
303                 case G_CONVERTER_ERROR:
304                         g_warning ("Error compressing response: %s",
305                                    error->message);
306                         g_error_free (error);
307                         g_object_unref (compressor);
308                         return;
309                 case G_CONVERTER_CONVERTED:
310                         converted += bytes_read;
311                         break;
312                 case G_CONVERTER_FINISHED:
313                         finished = TRUE;
314                         break;
315                 case G_CONVERTER_FLUSHED:
316                         break;
317                 }
318
319                 if (bytes_written)
320                         soup_message_body_append (msg->response_body,
321                                                   SOUP_MEMORY_COPY,
322                                                   buf, bytes_written);
323         }
324
325         g_object_unref (compressor);
326 }