68
69
static struct active_request_slot *active_queue_head;
70
71
+static char *cached_accept_language;
72
+
73
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
74
{
75
size_t size = eltsize * nmemb;
517
cert_auth.password = NULL;
518
}
519
ssl_cert_password_required = 0;
520
+
521
+ free(cached_accept_language);
522
+ cached_accept_language = NULL;
523
}
524
525
struct active_request_slot *get_active_slot(void)
991
strbuf_addstr(charset, "ISO-8859-1");
992
}
993
994
+/*
995
+ * Guess the user's preferred languages from the value in LANGUAGE environment
996
+ * variable and LC_MESSAGES locale category if NO_GETTEXT is not defined.
997
+ *
998
+ * The result can be a colon-separated list like "ko:ja:en".
999
+ */
1000
+static const char *get_preferred_languages(void)
1001
+{
1002
+ const char *retval;
1003
+
1004
+ retval = getenv("LANGUAGE");
1005
+ if (retval && *retval)
1006
+ return retval;
1007
+
1008
+#ifndef NO_GETTEXT
1009
+ retval = setlocale(LC_MESSAGES, NULL);
1010
+ if (retval && *retval &&
1011
+ strcmp(retval, "C") &&
1012
+ strcmp(retval, "POSIX"))
1013
+ return retval;
1014
+#endif
1015
+
1016
+ return NULL;
1017
+}
1018
+
1019
+static void write_accept_language(struct strbuf *buf)
1020
+{
1021
+ /*
1022
+ * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
1023
+ * that, q-value will be smaller than 0.001, the minimum q-value the
1024
+ * HTTP specification allows. See
1025
+ * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
1026
+ */
1027
+ const int MAX_DECIMAL_PLACES = 3;
1028
+ const int MAX_LANGUAGE_TAGS = 1000;
1029
+ const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000;
1030
+ char **language_tags = NULL;
1031
+ int num_langs = 0;
1032
+ const char *s = get_preferred_languages();
1033
+ int i;
1034
+ struct strbuf tag = STRBUF_INIT;
1035
+
1036
+ /* Don't add Accept-Language header if no language is preferred. */
1037
+ if (!s)
1038
+ return;
1039
+
1040
+ /*
1041
+ * Split the colon-separated string of preferred languages into
1042
+ * language_tags array.
1043
+ */
1044
+ do {
1045
+ /* collect language tag */
1046
+ for (; *s && (isalnum(*s) || *s == '_'); s++)
1047
+ strbuf_addch(&tag, *s == '_' ? '-' : *s);
1048
+
1049
+ /* skip .codeset, @modifier and any other unnecessary parts */
1050
+ while (*s && *s != ':')
1051
+ s++;
1052
+
1053
+ if (tag.len) {
1054
+ num_langs++;
1055
+ REALLOC_ARRAY(language_tags, num_langs);
1056
+ language_tags[num_langs - 1] = strbuf_detach(&tag, NULL);
1057
+ if (num_langs >= MAX_LANGUAGE_TAGS - 1) /* -1 for '*' */
1058
+ break;
1059
+ }
1060
+ } while (*s++);
1061
+
1062
+ /* write Accept-Language header into buf */
1063
+ if (num_langs) {
1064
+ int last_buf_len = 0;
1065
+ int max_q;
1066
+ int decimal_places;
1067
+ char q_format[32];
1068
+
1069
+ /* add '*' */
1070
+ REALLOC_ARRAY(language_tags, num_langs + 1);
1071
+ language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
1072
+
1073
+ /* compute decimal_places */
1074
+ for (max_q = 1, decimal_places = 0;
1075
+ max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;
1076
+ decimal_places++, max_q *= 10)
1077
+ ;
1078
+
1079
+ sprintf(q_format, ";q=0.%%0%dd", decimal_places);
1080
+
1081
+ strbuf_addstr(buf, "Accept-Language: ");
1082
+
1083
+ for (i = 0; i < num_langs; i++) {
1084
+ if (i > 0)
1085
+ strbuf_addstr(buf, ", ");
1086
+
1087
+ strbuf_addstr(buf, language_tags[i]);
1088
+
1089
+ if (i > 0)
1090
+ strbuf_addf(buf, q_format, max_q - i);
1091
+
1092
+ if (buf->len > MAX_ACCEPT_LANGUAGE_HEADER_SIZE) {
1093
+ strbuf_remove(buf, last_buf_len, buf->len - last_buf_len);
1094
+ break;
1095
+ }
1096
+
1097
+ last_buf_len = buf->len;
1098
+ }
1099
+ }
1100
+
1101
+ /* free language tags -- last one is a static '*' */
1102
+ for (i = 0; i < num_langs - 1; i++)
1103
+ free(language_tags[i]);
1104
+ free(language_tags);
1105
+}
1106
+
1107
+/*
1108
+ * Get an Accept-Language header which indicates user's preferred languages.
1109
+ *
1110
+ * Examples:
1111
+ * LANGUAGE= -> ""
1112
+ * LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
1113
+ * LANGUAGE=ko_KR.UTF-8:sr@latin -> "Accept-Language: ko-KR, sr; q=0.9, *; q=0.1"
1114
+ * LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
1115
+ * LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
1116
+ * LANGUAGE= LANG=C -> ""
1117
+ */
1118
+static const char *get_accept_language(void)
1119
+{
1120
+ if (!cached_accept_language) {
1121
+ struct strbuf buf = STRBUF_INIT;
1122
+ write_accept_language(&buf);
1123
+ if (buf.len > 0)
1124
+ cached_accept_language = strbuf_detach(&buf, NULL);
1125
+ }
1126
+
1127
+ return cached_accept_language;
1128
+}
1129
+
1130
/* http_request() targets */
1131
#define HTTP_REQUEST_STRBUF 0
1132
#define HTTP_REQUEST_FILE 1
1139
struct slot_results results;
1140
struct curl_slist *headers = NULL;
1141
struct strbuf buf = STRBUF_INIT;
1142
+ const char *accept_language;
1143
int ret;
1144
1145
slot = get_active_slot();
1165
fwrite_buffer);
1166
}
1167
1168
+ accept_language = get_accept_language();
1169
+
1170
+ if (accept_language)
1171
+ headers = curl_slist_append(headers, accept_language);
1172
+
1173
strbuf_addstr(&buf, "Pragma:");
1174
if (options && options->no_cache)
1175
strbuf_addstr(&buf, " no-cache");