@@ -68,6 +68,8 @@ static struct curl_slist *no_pragma_header;
68
68
69
69
static struct active_request_slot * active_queue_head ;
70
70
71
+ static char * cached_accept_language ;
72
+
71
73
size_t fread_buffer (char * ptr , size_t eltsize , size_t nmemb , void * buffer_ )
72
74
{
73
75
size_t size = eltsize * nmemb ;
@@ -515,6 +517,9 @@ void http_cleanup(void)
515
517
cert_auth .password = NULL ;
516
518
}
517
519
ssl_cert_password_required = 0 ;
520
+
521
+ free (cached_accept_language );
522
+ cached_accept_language = NULL ;
518
523
}
519
524
520
525
struct active_request_slot * get_active_slot (void )
@@ -986,6 +991,142 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,
986
991
strbuf_addstr (charset , "ISO-8859-1" );
987
992
}
988
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
+
989
1130
/* http_request() targets */
990
1131
#define HTTP_REQUEST_STRBUF 0
991
1132
#define HTTP_REQUEST_FILE 1
@@ -998,6 +1139,7 @@ static int http_request(const char *url,
998
1139
struct slot_results results ;
999
1140
struct curl_slist * headers = NULL ;
1000
1141
struct strbuf buf = STRBUF_INIT ;
1142
+ const char * accept_language ;
1001
1143
int ret ;
1002
1144
1003
1145
slot = get_active_slot ();
@@ -1023,6 +1165,11 @@ static int http_request(const char *url,
1023
1165
fwrite_buffer );
1024
1166
}
1025
1167
1168
+ accept_language = get_accept_language ();
1169
+
1170
+ if (accept_language )
1171
+ headers = curl_slist_append (headers , accept_language );
1172
+
1026
1173
strbuf_addstr (& buf , "Pragma:" );
1027
1174
if (options && options -> no_cache )
1028
1175
strbuf_addstr (& buf , " no-cache" );
0 commit comments