diff options
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 224 |
1 files changed, 144 insertions, 80 deletions
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | #include "snac.h" | 18 | #include "snac.h" |
| 19 | 19 | ||
| 20 | int login(snac *snac, const xs_dict *headers) | 20 | int login(snac *user, const xs_dict *headers) |
| 21 | /* tries a login */ | 21 | /* tries a login */ |
| 22 | { | 22 | { |
| 23 | int logged_in = 0; | 23 | int logged_in = 0; |
| @@ -31,23 +31,23 @@ int login(snac *snac, const xs_dict *headers) | |||
| 31 | xs *l1 = xs_split_n(s2, ":", 1); | 31 | xs *l1 = xs_split_n(s2, ":", 1); |
| 32 | 32 | ||
| 33 | if (xs_list_len(l1) == 2) { | 33 | if (xs_list_len(l1) == 2) { |
| 34 | const char *user = xs_list_get(l1, 0); | 34 | const char *uid = xs_list_get(l1, 0); |
| 35 | const char *pwd = xs_list_get(l1, 1); | 35 | const char *pwd = xs_list_get(l1, 1); |
| 36 | const char *addr = xs_or(xs_dict_get(headers, "remote-addr"), | 36 | const char *addr = xs_or(xs_dict_get(headers, "remote-addr"), |
| 37 | xs_dict_get(headers, "x-forwarded-for")); | 37 | xs_dict_get(headers, "x-forwarded-for")); |
| 38 | 38 | ||
| 39 | if (badlogin_check(user, addr)) { | 39 | if (badlogin_check(uid, addr)) { |
| 40 | logged_in = check_password(user, pwd, | 40 | logged_in = check_password(uid, pwd, |
| 41 | xs_dict_get(snac->config, "passwd")); | 41 | xs_dict_get(user->config, "passwd")); |
| 42 | 42 | ||
| 43 | if (!logged_in) | 43 | if (!logged_in) |
| 44 | badlogin_inc(user, addr); | 44 | badlogin_inc(uid, addr); |
| 45 | } | 45 | } |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | if (logged_in) | 49 | if (logged_in) |
| 50 | lastlog_write(snac, "web"); | 50 | lastlog_write(user, "web"); |
| 51 | 51 | ||
| 52 | return logged_in; | 52 | return logged_in; |
| 53 | } | 53 | } |
| @@ -69,7 +69,7 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 69 | 69 | ||
| 70 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); | 70 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); |
| 71 | 71 | ||
| 72 | const char *v; | 72 | const xs_dict *v; |
| 73 | int c = 0; | 73 | int c = 0; |
| 74 | 74 | ||
| 75 | while (xs_list_next(tag_list, &v, &c)) { | 75 | while (xs_list_next(tag_list, &v, &c)) { |
| @@ -77,19 +77,25 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 77 | 77 | ||
| 78 | if (t && strcmp(t, "Emoji") == 0) { | 78 | if (t && strcmp(t, "Emoji") == 0) { |
| 79 | const char *n = xs_dict_get(v, "name"); | 79 | const char *n = xs_dict_get(v, "name"); |
| 80 | const char *i = xs_dict_get(v, "icon"); | 80 | const xs_dict *i = xs_dict_get(v, "icon"); |
| 81 | 81 | ||
| 82 | if (n && i) { | 82 | if (xs_is_string(n) && xs_is_dict(i)) { |
| 83 | const char *u = xs_dict_get(i, "url"); | 83 | const char *u = xs_dict_get(i, "url"); |
| 84 | xs *url = make_url(u, proxy, 0); | 84 | const char *mt = xs_dict_get(i, "mediaType"); |
| 85 | |||
| 86 | if (xs_is_string(u) && xs_is_string(mt) && strcmp(mt, "image/svg+xml")) { | ||
| 87 | xs *url = make_url(u, proxy, 0); | ||
| 85 | 88 | ||
| 86 | xs_html *img = xs_html_sctag("img", | 89 | xs_html *img = xs_html_sctag("img", |
| 87 | xs_html_attr("loading", "lazy"), | 90 | xs_html_attr("loading", "lazy"), |
| 88 | xs_html_attr("src", url), | 91 | xs_html_attr("src", url), |
| 89 | xs_html_attr("style", style)); | 92 | xs_html_attr("style", style)); |
| 90 | 93 | ||
| 91 | xs *s1 = xs_html_render(img); | 94 | xs *s1 = xs_html_render(img); |
| 92 | s = xs_replace_i(s, n, s1); | 95 | s = xs_replace_i(s, n, s1); |
| 96 | } | ||
| 97 | else | ||
| 98 | s = xs_replace_i(s, n, ""); | ||
| 93 | } | 99 | } |
| 94 | } | 100 | } |
| 95 | } | 101 | } |
| @@ -621,6 +627,9 @@ static xs_html *html_instance_body(void) | |||
| 621 | const char *email = xs_dict_get(srv_config, "admin_email"); | 627 | const char *email = xs_dict_get(srv_config, "admin_email"); |
| 622 | const char *acct = xs_dict_get(srv_config, "admin_account"); | 628 | const char *acct = xs_dict_get(srv_config, "admin_account"); |
| 623 | 629 | ||
| 630 | /* for L() */ | ||
| 631 | const snac *user = NULL; | ||
| 632 | |||
| 624 | xs *blurb = xs_replace(snac_blurb, "%host%", host); | 633 | xs *blurb = xs_replace(snac_blurb, "%host%", host); |
| 625 | 634 | ||
| 626 | xs_html *dl; | 635 | xs_html *dl; |
| @@ -735,9 +744,11 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) | |||
| 735 | xs *fwers = follower_list(user); | 744 | xs *fwers = follower_list(user); |
| 736 | xs *fwing = following_list(user); | 745 | xs *fwing = following_list(user); |
| 737 | 746 | ||
| 738 | xs *s1 = xs_fmt(L("%d following, %d followers · "), | 747 | xs *s1 = xs_fmt(L("%d following, %d followers"), |
| 739 | xs_list_len(fwing), xs_list_len(fwers)); | 748 | xs_list_len(fwing), xs_list_len(fwers)); |
| 740 | 749 | ||
| 750 | s1 = xs_str_cat(s1, " · "); | ||
| 751 | |||
| 741 | s_desc = xs_str_prepend_i(s_desc, s1); | 752 | s_desc = xs_str_prepend_i(s_desc, s1); |
| 742 | } | 753 | } |
| 743 | 754 | ||
| @@ -1052,8 +1063,8 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1052 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); | 1063 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); |
| 1053 | 1064 | ||
| 1054 | if (*latitude && *longitude) { | 1065 | if (*latitude && *longitude) { |
| 1055 | xs *label = xs_fmt(L("%s,%s"), latitude, longitude); | 1066 | xs *label = xs_fmt("%s,%s", latitude, longitude); |
| 1056 | xs *url = xs_fmt(L("https://openstreetmap.org/search?query=%s,%s"), | 1067 | xs *url = xs_fmt("https://openstreetmap.org/search?query=%s,%s", |
| 1057 | latitude, longitude); | 1068 | latitude, longitude); |
| 1058 | 1069 | ||
| 1059 | xs_html_add(top_user, | 1070 | xs_html_add(top_user, |
| @@ -1069,7 +1080,7 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1069 | xs *fwers = follower_list(user); | 1080 | xs *fwers = follower_list(user); |
| 1070 | xs *fwing = following_list(user); | 1081 | xs *fwing = following_list(user); |
| 1071 | 1082 | ||
| 1072 | xs *s1 = xs_fmt(L("%d following %d followers"), | 1083 | xs *s1 = xs_fmt(L("%d following, %d followers"), |
| 1073 | xs_list_len(fwing), xs_list_len(fwers)); | 1084 | xs_list_len(fwing), xs_list_len(fwers)); |
| 1074 | 1085 | ||
| 1075 | xs_html_add(top_user, | 1086 | xs_html_add(top_user, |
| @@ -1085,16 +1096,16 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1085 | } | 1096 | } |
| 1086 | 1097 | ||
| 1087 | 1098 | ||
| 1088 | xs_html *html_top_controls(snac *snac) | 1099 | xs_html *html_top_controls(snac *user) |
| 1089 | /* generates the top controls */ | 1100 | /* generates the top controls */ |
| 1090 | { | 1101 | { |
| 1091 | xs *ops_action = xs_fmt("%s/admin/action", snac->actor); | 1102 | xs *ops_action = xs_fmt("%s/admin/action", user->actor); |
| 1092 | 1103 | ||
| 1093 | xs_html *top_controls = xs_html_tag("div", | 1104 | xs_html *top_controls = xs_html_tag("div", |
| 1094 | xs_html_attr("class", "snac-top-controls"), | 1105 | xs_html_attr("class", "snac-top-controls"), |
| 1095 | 1106 | ||
| 1096 | /** new post **/ | 1107 | /** new post **/ |
| 1097 | html_note(snac, L("New Post..."), | 1108 | html_note(user, L("New Post..."), |
| 1098 | "new_post_div", "new_post_form", | 1109 | "new_post_div", "new_post_form", |
| 1099 | L("What's on your mind?"), "", | 1110 | L("What's on your mind?"), "", |
| 1100 | NULL, NULL, | 1111 | NULL, NULL, |
| @@ -1164,53 +1175,53 @@ xs_html *html_top_controls(snac *snac) | |||
| 1164 | const char *email = "[disabled by admin]"; | 1175 | const char *email = "[disabled by admin]"; |
| 1165 | 1176 | ||
| 1166 | if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { | 1177 | if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { |
| 1167 | email = xs_dict_get(snac->config_o, "email"); | 1178 | email = xs_dict_get(user->config_o, "email"); |
| 1168 | if (xs_is_null(email)) { | 1179 | if (xs_is_null(email)) { |
| 1169 | email = xs_dict_get(snac->config, "email"); | 1180 | email = xs_dict_get(user->config, "email"); |
| 1170 | 1181 | ||
| 1171 | if (xs_is_null(email)) | 1182 | if (xs_is_null(email)) |
| 1172 | email = ""; | 1183 | email = ""; |
| 1173 | } | 1184 | } |
| 1174 | } | 1185 | } |
| 1175 | 1186 | ||
| 1176 | const char *cw = xs_dict_get(snac->config, "cw"); | 1187 | const char *cw = xs_dict_get(user->config, "cw"); |
| 1177 | if (xs_is_null(cw)) | 1188 | if (xs_is_null(cw)) |
| 1178 | cw = ""; | 1189 | cw = ""; |
| 1179 | 1190 | ||
| 1180 | const char *telegram_bot = xs_dict_get(snac->config, "telegram_bot"); | 1191 | const char *telegram_bot = xs_dict_get(user->config, "telegram_bot"); |
| 1181 | if (xs_is_null(telegram_bot)) | 1192 | if (xs_is_null(telegram_bot)) |
| 1182 | telegram_bot = ""; | 1193 | telegram_bot = ""; |
| 1183 | 1194 | ||
| 1184 | const char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id"); | 1195 | const char *telegram_chat_id = xs_dict_get(user->config, "telegram_chat_id"); |
| 1185 | if (xs_is_null(telegram_chat_id)) | 1196 | if (xs_is_null(telegram_chat_id)) |
| 1186 | telegram_chat_id = ""; | 1197 | telegram_chat_id = ""; |
| 1187 | 1198 | ||
| 1188 | const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); | 1199 | const char *ntfy_server = xs_dict_get(user->config, "ntfy_server"); |
| 1189 | if (xs_is_null(ntfy_server)) | 1200 | if (xs_is_null(ntfy_server)) |
| 1190 | ntfy_server = ""; | 1201 | ntfy_server = ""; |
| 1191 | 1202 | ||
| 1192 | const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); | 1203 | const char *ntfy_token = xs_dict_get(user->config, "ntfy_token"); |
| 1193 | if (xs_is_null(ntfy_token)) | 1204 | if (xs_is_null(ntfy_token)) |
| 1194 | ntfy_token = ""; | 1205 | ntfy_token = ""; |
| 1195 | 1206 | ||
| 1196 | const char *purge_days = xs_dict_get(snac->config, "purge_days"); | 1207 | const char *purge_days = xs_dict_get(user->config, "purge_days"); |
| 1197 | if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) | 1208 | if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) |
| 1198 | purge_days = (char *)xs_number_str(purge_days); | 1209 | purge_days = (char *)xs_number_str(purge_days); |
| 1199 | else | 1210 | else |
| 1200 | purge_days = "0"; | 1211 | purge_days = "0"; |
| 1201 | 1212 | ||
| 1202 | const xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown"); | 1213 | const xs_val *d_dm_f_u = xs_dict_get(user->config, "drop_dm_from_unknown"); |
| 1203 | const xs_val *bot = xs_dict_get(snac->config, "bot"); | 1214 | const xs_val *bot = xs_dict_get(user->config, "bot"); |
| 1204 | const xs_val *a_private = xs_dict_get(snac->config, "private"); | 1215 | const xs_val *a_private = xs_dict_get(user->config, "private"); |
| 1205 | const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); | 1216 | const xs_val *auto_boost = xs_dict_get(user->config, "auto_boost"); |
| 1206 | const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); | 1217 | const xs_val *coll_thrds = xs_dict_get(user->config, "collapse_threads"); |
| 1207 | const xs_val *pending = xs_dict_get(snac->config, "approve_followers"); | 1218 | const xs_val *pending = xs_dict_get(user->config, "approve_followers"); |
| 1208 | const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics"); | 1219 | const xs_val *show_foll = xs_dict_get(user->config, "show_contact_metrics"); |
| 1209 | const char *latitude = xs_dict_get_def(snac->config, "latitude", ""); | 1220 | const char *latitude = xs_dict_get_def(user->config, "latitude", ""); |
| 1210 | const char *longitude = xs_dict_get_def(snac->config, "longitude", ""); | 1221 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); |
| 1211 | 1222 | ||
| 1212 | xs *metadata = NULL; | 1223 | xs *metadata = NULL; |
| 1213 | const xs_dict *md = xs_dict_get(snac->config, "metadata"); | 1224 | const xs_dict *md = xs_dict_get(user->config, "metadata"); |
| 1214 | 1225 | ||
| 1215 | if (xs_type(md) == XSTYPE_DICT) { | 1226 | if (xs_type(md) == XSTYPE_DICT) { |
| 1216 | const xs_str *k; | 1227 | const xs_str *k; |
| @@ -1232,7 +1243,29 @@ xs_html *html_top_controls(snac *snac) | |||
| 1232 | else | 1243 | else |
| 1233 | metadata = xs_str_new(NULL); | 1244 | metadata = xs_str_new(NULL); |
| 1234 | 1245 | ||
| 1235 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor); | 1246 | /* ui language */ |
| 1247 | xs_html *lang_select = xs_html_tag("select", | ||
| 1248 | xs_html_attr("name", "web_ui_lang")); | ||
| 1249 | |||
| 1250 | const char *u_lang = xs_dict_get_def(user->config, "lang", "en"); | ||
| 1251 | const char *lang; | ||
| 1252 | const xs_dict *langs; | ||
| 1253 | |||
| 1254 | xs_dict_foreach(srv_langs, lang, langs) { | ||
| 1255 | if (strcmp(u_lang, lang) == 0) | ||
| 1256 | xs_html_add(lang_select, | ||
| 1257 | xs_html_tag("option", | ||
| 1258 | xs_html_text(lang), | ||
| 1259 | xs_html_attr("value", lang), | ||
| 1260 | xs_html_attr("selected", "selected"))); | ||
| 1261 | else | ||
| 1262 | xs_html_add(lang_select, | ||
| 1263 | xs_html_tag("option", | ||
| 1264 | xs_html_text(lang), | ||
| 1265 | xs_html_attr("value", lang))); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); | ||
| 1236 | 1269 | ||
| 1237 | xs_html_add(top_controls, | 1270 | xs_html_add(top_controls, |
| 1238 | xs_html_tag("details", | 1271 | xs_html_tag("details", |
| @@ -1251,7 +1284,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1251 | xs_html_sctag("input", | 1284 | xs_html_sctag("input", |
| 1252 | xs_html_attr("type", "text"), | 1285 | xs_html_attr("type", "text"), |
| 1253 | xs_html_attr("name", "name"), | 1286 | xs_html_attr("name", "name"), |
| 1254 | xs_html_attr("value", xs_dict_get(snac->config, "name")), | 1287 | xs_html_attr("value", xs_dict_get(user->config, "name")), |
| 1255 | xs_html_attr("placeholder", L("Your name")))), | 1288 | xs_html_attr("placeholder", L("Your name")))), |
| 1256 | xs_html_tag("p", | 1289 | xs_html_tag("p", |
| 1257 | xs_html_text(L("Avatar: ")), | 1290 | xs_html_text(L("Avatar: ")), |
| @@ -1281,7 +1314,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1281 | xs_html_attr("cols", "40"), | 1314 | xs_html_attr("cols", "40"), |
| 1282 | xs_html_attr("rows", "4"), | 1315 | xs_html_attr("rows", "4"), |
| 1283 | xs_html_attr("placeholder", L("Write about yourself here...")), | 1316 | xs_html_attr("placeholder", L("Write about yourself here...")), |
| 1284 | xs_html_text(xs_dict_get(snac->config, "bio")))), | 1317 | xs_html_text(xs_dict_get(user->config, "bio")))), |
| 1285 | xs_html_sctag("input", | 1318 | xs_html_sctag("input", |
| 1286 | xs_html_attr("type", "checkbox"), | 1319 | xs_html_attr("type", "checkbox"), |
| 1287 | xs_html_attr("name", "cw"), | 1320 | xs_html_attr("name", "cw"), |
| @@ -1423,6 +1456,11 @@ xs_html *html_top_controls(snac *snac) | |||
| 1423 | xs_html_text(metadata))), | 1456 | xs_html_text(metadata))), |
| 1424 | 1457 | ||
| 1425 | xs_html_tag("p", | 1458 | xs_html_tag("p", |
| 1459 | xs_html_text(L("Web interface language:")), | ||
| 1460 | xs_html_sctag("br", NULL), | ||
| 1461 | lang_select), | ||
| 1462 | |||
| 1463 | xs_html_tag("p", | ||
| 1426 | xs_html_text(L("New password:")), | 1464 | xs_html_text(L("New password:")), |
| 1427 | xs_html_sctag("br", NULL), | 1465 | xs_html_sctag("br", NULL), |
| 1428 | xs_html_sctag("input", | 1466 | xs_html_sctag("input", |
| @@ -1444,8 +1482,8 @@ xs_html *html_top_controls(snac *snac) | |||
| 1444 | 1482 | ||
| 1445 | xs_html_tag("p", NULL))))); | 1483 | xs_html_tag("p", NULL))))); |
| 1446 | 1484 | ||
| 1447 | xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", snac->actor); | 1485 | xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", user->actor); |
| 1448 | xs *followed_hashtags = xs_join(xs_dict_get_def(snac->config, | 1486 | xs *followed_hashtags = xs_join(xs_dict_get_def(user->config, |
| 1449 | "followed_hashtags", xs_stock(XSTYPE_LIST)), "\n"); | 1487 | "followed_hashtags", xs_stock(XSTYPE_LIST)), "\n"); |
| 1450 | 1488 | ||
| 1451 | xs_html_add(top_controls, | 1489 | xs_html_add(top_controls, |
| @@ -1480,7 +1518,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1480 | } | 1518 | } |
| 1481 | 1519 | ||
| 1482 | 1520 | ||
| 1483 | static xs_html *html_button(char *clss, char *label, char *hint) | 1521 | static xs_html *html_button(const char *clss, const char *label, const char *hint) |
| 1484 | { | 1522 | { |
| 1485 | xs *c = xs_fmt("snac-btn-%s", clss); | 1523 | xs *c = xs_fmt("snac-btn-%s", clss); |
| 1486 | 1524 | ||
| @@ -1496,7 +1534,7 @@ static xs_html *html_button(char *clss, char *label, char *hint) | |||
| 1496 | } | 1534 | } |
| 1497 | 1535 | ||
| 1498 | 1536 | ||
| 1499 | xs_str *build_mentions(snac *snac, const xs_dict *msg) | 1537 | xs_str *build_mentions(snac *user, const xs_dict *msg) |
| 1500 | /* returns a string with the mentions in msg */ | 1538 | /* returns a string with the mentions in msg */ |
| 1501 | { | 1539 | { |
| 1502 | xs_str *s = xs_str_new(NULL); | 1540 | xs_str *s = xs_str_new(NULL); |
| @@ -1510,7 +1548,7 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) | |||
| 1510 | const char *name = xs_dict_get(v, "name"); | 1548 | const char *name = xs_dict_get(v, "name"); |
| 1511 | 1549 | ||
| 1512 | if (type && strcmp(type, "Mention") == 0 && | 1550 | if (type && strcmp(type, "Mention") == 0 && |
| 1513 | href && strcmp(href, snac->actor) != 0 && name) { | 1551 | href && strcmp(href, user->actor) != 0 && name) { |
| 1514 | xs *s1 = NULL; | 1552 | xs *s1 = NULL; |
| 1515 | 1553 | ||
| 1516 | if (name[0] != '@') { | 1554 | if (name[0] != '@') { |
| @@ -1551,7 +1589,7 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) | |||
| 1551 | } | 1589 | } |
| 1552 | 1590 | ||
| 1553 | 1591 | ||
| 1554 | xs_html *html_entry_controls(snac *snac, const char *actor, | 1592 | xs_html *html_entry_controls(snac *user, const char *actor, |
| 1555 | const xs_dict *msg, const char *md5) | 1593 | const xs_dict *msg, const char *md5) |
| 1556 | { | 1594 | { |
| 1557 | const char *id = xs_dict_get(msg, "id"); | 1595 | const char *id = xs_dict_get(msg, "id"); |
| @@ -1560,7 +1598,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1560 | xs *likes = object_likes(id); | 1598 | xs *likes = object_likes(id); |
| 1561 | xs *boosts = object_announces(id); | 1599 | xs *boosts = object_announces(id); |
| 1562 | 1600 | ||
| 1563 | xs *action = xs_fmt("%s/admin/action", snac->actor); | 1601 | xs *action = xs_fmt("%s/admin/action", user->actor); |
| 1564 | xs *redir = xs_fmt("%s_entry", md5); | 1602 | xs *redir = xs_fmt("%s_entry", md5); |
| 1565 | 1603 | ||
| 1566 | xs_html *form; | 1604 | xs_html *form; |
| @@ -1587,8 +1625,8 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1587 | xs_html_attr("name", "redir"), | 1625 | xs_html_attr("name", "redir"), |
| 1588 | xs_html_attr("value", redir)))); | 1626 | xs_html_attr("value", redir)))); |
| 1589 | 1627 | ||
| 1590 | if (!xs_startswith(id, snac->actor)) { | 1628 | if (!xs_startswith(id, user->actor)) { |
| 1591 | if (xs_list_in(likes, snac->md5) == -1) { | 1629 | if (xs_list_in(likes, user->md5) == -1) { |
| 1592 | /* not already liked; add button */ | 1630 | /* not already liked; add button */ |
| 1593 | xs_html_add(form, | 1631 | xs_html_add(form, |
| 1594 | html_button("like", L("Like"), L("Say you like this post"))); | 1632 | html_button("like", L("Like"), L("Say you like this post"))); |
| @@ -1600,7 +1638,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1600 | } | 1638 | } |
| 1601 | } | 1639 | } |
| 1602 | else { | 1640 | else { |
| 1603 | if (is_pinned(snac, id)) | 1641 | if (is_pinned(user, id)) |
| 1604 | xs_html_add(form, | 1642 | xs_html_add(form, |
| 1605 | html_button("unpin", L("Unpin"), L("Unpin this post from your timeline"))); | 1643 | html_button("unpin", L("Unpin"), L("Unpin this post from your timeline"))); |
| 1606 | else | 1644 | else |
| @@ -1609,7 +1647,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1609 | } | 1647 | } |
| 1610 | 1648 | ||
| 1611 | if (is_msg_public(msg)) { | 1649 | if (is_msg_public(msg)) { |
| 1612 | if (xs_list_in(boosts, snac->md5) == -1) { | 1650 | if (xs_list_in(boosts, user->md5) == -1) { |
| 1613 | /* not already boosted; add button */ | 1651 | /* not already boosted; add button */ |
| 1614 | xs_html_add(form, | 1652 | xs_html_add(form, |
| 1615 | html_button("boost", L("Boost"), L("Announce this post to your followers"))); | 1653 | html_button("boost", L("Boost"), L("Announce this post to your followers"))); |
| @@ -1621,16 +1659,16 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1621 | } | 1659 | } |
| 1622 | } | 1660 | } |
| 1623 | 1661 | ||
| 1624 | if (is_bookmarked(snac, id)) | 1662 | if (is_bookmarked(user, id)) |
| 1625 | xs_html_add(form, | 1663 | xs_html_add(form, |
| 1626 | html_button("unbookmark", L("Unbookmark"), L("Delete this post from your bookmarks"))); | 1664 | html_button("unbookmark", L("Unbookmark"), L("Delete this post from your bookmarks"))); |
| 1627 | else | 1665 | else |
| 1628 | xs_html_add(form, | 1666 | xs_html_add(form, |
| 1629 | html_button("bookmark", L("Bookmark"), L("Add this post to your bookmarks"))); | 1667 | html_button("bookmark", L("Bookmark"), L("Add this post to your bookmarks"))); |
| 1630 | 1668 | ||
| 1631 | if (strcmp(actor, snac->actor) != 0) { | 1669 | if (strcmp(actor, user->actor) != 0) { |
| 1632 | /* controls for other actors than this one */ | 1670 | /* controls for other actors than this one */ |
| 1633 | if (following_check(snac, actor)) { | 1671 | if (following_check(user, actor)) { |
| 1634 | xs_html_add(form, | 1672 | xs_html_add(form, |
| 1635 | html_button("unfollow", L("Unfollow"), L("Stop following this user's activity"))); | 1673 | html_button("unfollow", L("Unfollow"), L("Stop following this user's activity"))); |
| 1636 | } | 1674 | } |
| @@ -1640,7 +1678,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1640 | } | 1678 | } |
| 1641 | 1679 | ||
| 1642 | if (!xs_is_null(group)) { | 1680 | if (!xs_is_null(group)) { |
| 1643 | if (following_check(snac, group)) { | 1681 | if (following_check(user, group)) { |
| 1644 | xs_html_add(form, | 1682 | xs_html_add(form, |
| 1645 | html_button("unfollow", L("Unfollow Group"), | 1683 | html_button("unfollow", L("Unfollow Group"), |
| 1646 | L("Stop following this group or channel"))); | 1684 | L("Stop following this group or channel"))); |
| @@ -1666,7 +1704,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1666 | 1704 | ||
| 1667 | const char *prev_src = xs_dict_get(msg, "sourceContent"); | 1705 | const char *prev_src = xs_dict_get(msg, "sourceContent"); |
| 1668 | 1706 | ||
| 1669 | if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/ | 1707 | if (!xs_is_null(prev_src) && strcmp(actor, user->actor) == 0) { /** edit **/ |
| 1670 | /* post can be edited */ | 1708 | /* post can be edited */ |
| 1671 | xs *div_id = xs_fmt("%s_edit", md5); | 1709 | xs *div_id = xs_fmt("%s_edit", md5); |
| 1672 | xs *form_id = xs_fmt("%s_edit_form", md5); | 1710 | xs *form_id = xs_fmt("%s_edit_form", md5); |
| @@ -1693,26 +1731,26 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1693 | 1731 | ||
| 1694 | xs_html_add(controls, xs_html_tag("div", | 1732 | xs_html_add(controls, xs_html_tag("div", |
| 1695 | xs_html_tag("p", NULL), | 1733 | xs_html_tag("p", NULL), |
| 1696 | html_note(snac, L("Edit..."), | 1734 | html_note(user, L("Edit..."), |
| 1697 | div_id, form_id, | 1735 | div_id, form_id, |
| 1698 | "", prev_src, | 1736 | "", prev_src, |
| 1699 | id, NULL, | 1737 | id, NULL, |
| 1700 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), | 1738 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), |
| 1701 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, | 1739 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, |
| 1702 | NULL, 0, att_files, att_alt_texts, is_draft(snac, id))), | 1740 | NULL, 0, att_files, att_alt_texts, is_draft(user, id))), |
| 1703 | xs_html_tag("p", NULL)); | 1741 | xs_html_tag("p", NULL)); |
| 1704 | } | 1742 | } |
| 1705 | 1743 | ||
| 1706 | { /** reply **/ | 1744 | { /** reply **/ |
| 1707 | /* the post textarea */ | 1745 | /* the post textarea */ |
| 1708 | xs *ct = build_mentions(snac, msg); | 1746 | xs *ct = build_mentions(user, msg); |
| 1709 | xs *div_id = xs_fmt("%s_reply", md5); | 1747 | xs *div_id = xs_fmt("%s_reply", md5); |
| 1710 | xs *form_id = xs_fmt("%s_reply_form", md5); | 1748 | xs *form_id = xs_fmt("%s_reply_form", md5); |
| 1711 | xs *redir = xs_fmt("%s_entry", md5); | 1749 | xs *redir = xs_fmt("%s_entry", md5); |
| 1712 | 1750 | ||
| 1713 | xs_html_add(controls, xs_html_tag("div", | 1751 | xs_html_add(controls, xs_html_tag("div", |
| 1714 | xs_html_tag("p", NULL), | 1752 | xs_html_tag("p", NULL), |
| 1715 | html_note(snac, L("Reply..."), | 1753 | html_note(user, L("Reply..."), |
| 1716 | div_id, form_id, | 1754 | div_id, form_id, |
| 1717 | "", ct, | 1755 | "", ct, |
| 1718 | NULL, NULL, | 1756 | NULL, NULL, |
| @@ -1839,7 +1877,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1839 | xs_html_raw(" 📌 "))); | 1877 | xs_html_raw(" 📌 "))); |
| 1840 | } | 1878 | } |
| 1841 | 1879 | ||
| 1842 | if (user && is_bookmarked(user, id)) { | 1880 | if (user && !read_only && is_bookmarked(user, id)) { |
| 1843 | /* add a bookmark emoji */ | 1881 | /* add a bookmark emoji */ |
| 1844 | xs_html_add(score, | 1882 | xs_html_add(score, |
| 1845 | xs_html_tag("span", | 1883 | xs_html_tag("span", |
| @@ -2242,6 +2280,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2242 | if (content && xs_str_in(content, o_href) != -1) | 2280 | if (content && xs_str_in(content, o_href) != -1) |
| 2243 | continue; | 2281 | continue; |
| 2244 | 2282 | ||
| 2283 | /* drop silently any attachment that may include JavaScript */ | ||
| 2284 | if (strcmp(type, "image/svg+xml") == 0 || | ||
| 2285 | strcmp(type, "text/html") == 0) | ||
| 2286 | continue; | ||
| 2287 | |||
| 2245 | /* do this attachment include an icon? */ | 2288 | /* do this attachment include an icon? */ |
| 2246 | const xs_dict *icon = xs_dict_get(a, "icon"); | 2289 | const xs_dict *icon = xs_dict_get(a, "icon"); |
| 2247 | if (xs_type(icon) == XSTYPE_DICT) { | 2290 | if (xs_type(icon) == XSTYPE_DICT) { |
| @@ -2571,7 +2614,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2571 | } | 2614 | } |
| 2572 | 2615 | ||
| 2573 | 2616 | ||
| 2574 | xs_html *html_footer(void) | 2617 | xs_html *html_footer(const snac *user) |
| 2575 | { | 2618 | { |
| 2576 | return xs_html_tag("div", | 2619 | return xs_html_tag("div", |
| 2577 | xs_html_attr("class", "snac-footer"), | 2620 | xs_html_attr("class", "snac-footer"), |
| @@ -2879,13 +2922,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 2879 | } | 2922 | } |
| 2880 | 2923 | ||
| 2881 | xs_html_add(body, | 2924 | xs_html_add(body, |
| 2882 | html_footer()); | 2925 | html_footer(user)); |
| 2883 | 2926 | ||
| 2884 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 2927 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 2885 | } | 2928 | } |
| 2886 | 2929 | ||
| 2887 | 2930 | ||
| 2888 | xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, const char *proxy) | 2931 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy) |
| 2889 | { | 2932 | { |
| 2890 | xs_html *snac_posts; | 2933 | xs_html *snac_posts; |
| 2891 | xs_html *people = xs_html_tag("div", | 2934 | xs_html *people = xs_html_tag("div", |
| @@ -2910,7 +2953,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2910 | xs_html_attr("name", md5)), | 2953 | xs_html_attr("name", md5)), |
| 2911 | xs_html_tag("div", | 2954 | xs_html_tag("div", |
| 2912 | xs_html_attr("class", "snac-post-header"), | 2955 | xs_html_attr("class", "snac-post-header"), |
| 2913 | html_actor_icon(snac, actor, xs_dict_get(actor, "published"), | 2956 | html_actor_icon(user, actor, xs_dict_get(actor, "published"), |
| 2914 | NULL, NULL, 0, 1, proxy, NULL, NULL))); | 2957 | NULL, NULL, 0, 1, proxy, NULL, NULL))); |
| 2915 | 2958 | ||
| 2916 | /* content (user bio) */ | 2959 | /* content (user bio) */ |
| @@ -2934,7 +2977,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2934 | } | 2977 | } |
| 2935 | 2978 | ||
| 2936 | /* buttons */ | 2979 | /* buttons */ |
| 2937 | xs *btn_form_action = xs_fmt("%s/admin/action", snac->actor); | 2980 | xs *btn_form_action = xs_fmt("%s/admin/action", user->actor); |
| 2938 | 2981 | ||
| 2939 | xs_html *snac_controls = xs_html_tag("div", | 2982 | xs_html *snac_controls = xs_html_tag("div", |
| 2940 | xs_html_attr("class", "snac-controls")); | 2983 | xs_html_attr("class", "snac-controls")); |
| @@ -2954,12 +2997,12 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2954 | 2997 | ||
| 2955 | xs_html_add(snac_controls, form); | 2998 | xs_html_add(snac_controls, form); |
| 2956 | 2999 | ||
| 2957 | if (following_check(snac, actor_id)) { | 3000 | if (following_check(user, actor_id)) { |
| 2958 | xs_html_add(form, | 3001 | xs_html_add(form, |
| 2959 | html_button("unfollow", L("Unfollow"), | 3002 | html_button("unfollow", L("Unfollow"), |
| 2960 | L("Stop following this user's activity"))); | 3003 | L("Stop following this user's activity"))); |
| 2961 | 3004 | ||
| 2962 | if (is_limited(snac, actor_id)) | 3005 | if (is_limited(user, actor_id)) |
| 2963 | xs_html_add(form, | 3006 | xs_html_add(form, |
| 2964 | html_button("unlimit", L("Unlimit"), | 3007 | html_button("unlimit", L("Unlimit"), |
| 2965 | L("Allow announces (boosts) from this user"))); | 3008 | L("Allow announces (boosts) from this user"))); |
| @@ -2973,12 +3016,12 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2973 | html_button("follow", L("Follow"), | 3016 | html_button("follow", L("Follow"), |
| 2974 | L("Start following this user's activity"))); | 3017 | L("Start following this user's activity"))); |
| 2975 | 3018 | ||
| 2976 | if (follower_check(snac, actor_id)) | 3019 | if (follower_check(user, actor_id)) |
| 2977 | xs_html_add(form, | 3020 | xs_html_add(form, |
| 2978 | html_button("delete", L("Delete"), L("Delete this user"))); | 3021 | html_button("delete", L("Delete"), L("Delete this user"))); |
| 2979 | } | 3022 | } |
| 2980 | 3023 | ||
| 2981 | if (pending_check(snac, actor_id)) { | 3024 | if (pending_check(user, actor_id)) { |
| 2982 | xs_html_add(form, | 3025 | xs_html_add(form, |
| 2983 | html_button("approve", L("Approve"), | 3026 | html_button("approve", L("Approve"), |
| 2984 | L("Approve this follow request"))); | 3027 | L("Approve this follow request"))); |
| @@ -2987,7 +3030,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2987 | html_button("discard", L("Discard"), L("Discard this follow request"))); | 3030 | html_button("discard", L("Discard"), L("Discard this follow request"))); |
| 2988 | } | 3031 | } |
| 2989 | 3032 | ||
| 2990 | if (is_muted(snac, actor_id)) | 3033 | if (is_muted(user, actor_id)) |
| 2991 | xs_html_add(form, | 3034 | xs_html_add(form, |
| 2992 | html_button("unmute", L("Unmute"), | 3035 | html_button("unmute", L("Unmute"), |
| 2993 | L("Stop blocking activities from this user"))); | 3036 | L("Stop blocking activities from this user"))); |
| @@ -3002,7 +3045,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 3002 | 3045 | ||
| 3003 | xs_html_add(snac_controls, | 3046 | xs_html_add(snac_controls, |
| 3004 | xs_html_tag("p", NULL), | 3047 | xs_html_tag("p", NULL), |
| 3005 | html_note(snac, L("Direct Message..."), | 3048 | html_note(user, L("Direct Message..."), |
| 3006 | dm_div_id, dm_form_id, | 3049 | dm_div_id, dm_form_id, |
| 3007 | "", "", | 3050 | "", "", |
| 3008 | NULL, actor_id, | 3051 | NULL, actor_id, |
| @@ -3048,7 +3091,7 @@ xs_str *html_people(snac *user) | |||
| 3048 | html_user_head(user, NULL, NULL), | 3091 | html_user_head(user, NULL, NULL), |
| 3049 | xs_html_add(html_user_body(user, 0), | 3092 | xs_html_add(html_user_body(user, 0), |
| 3050 | lists, | 3093 | lists, |
| 3051 | html_footer())); | 3094 | html_footer(user))); |
| 3052 | 3095 | ||
| 3053 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 3096 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3054 | } | 3097 | } |
| @@ -3298,7 +3341,7 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3298 | xs_set_free(&rep); | 3341 | xs_set_free(&rep); |
| 3299 | 3342 | ||
| 3300 | xs_html_add(body, | 3343 | xs_html_add(body, |
| 3301 | html_footer()); | 3344 | html_footer(user)); |
| 3302 | 3345 | ||
| 3303 | /* set the check time to now */ | 3346 | /* set the check time to now */ |
| 3304 | xs *dummy = notify_check_time(user, 1); | 3347 | xs *dummy = notify_check_time(user, 1); |
| @@ -3310,12 +3353,24 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3310 | } | 3353 | } |
| 3311 | 3354 | ||
| 3312 | 3355 | ||
| 3356 | void set_user_lang(snac *user) | ||
| 3357 | /* sets the language dict according to user configuration */ | ||
| 3358 | { | ||
| 3359 | user->lang = NULL; | ||
| 3360 | const char *lang = xs_dict_get(user->config, "lang"); | ||
| 3361 | |||
| 3362 | if (xs_is_string(lang)) | ||
| 3363 | user->lang = xs_dict_get(srv_langs, lang); | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | |||
| 3313 | int html_get_handler(const xs_dict *req, const char *q_path, | 3367 | int html_get_handler(const xs_dict *req, const char *q_path, |
| 3314 | char **body, int *b_size, char **ctype, | 3368 | char **body, int *b_size, char **ctype, |
| 3315 | xs_str **etag, xs_str **last_modified) | 3369 | xs_str **etag, xs_str **last_modified) |
| 3316 | { | 3370 | { |
| 3317 | const char *accept = xs_dict_get(req, "accept"); | 3371 | const char *accept = xs_dict_get(req, "accept"); |
| 3318 | int status = HTTP_STATUS_NOT_FOUND; | 3372 | int status = HTTP_STATUS_NOT_FOUND; |
| 3373 | const snac *user = NULL; | ||
| 3319 | snac snac; | 3374 | snac snac; |
| 3320 | xs *uid = NULL; | 3375 | xs *uid = NULL; |
| 3321 | const char *p_path; | 3376 | const char *p_path; |
| @@ -3382,6 +3437,9 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3382 | return HTTP_STATUS_NOT_FOUND; | 3437 | return HTTP_STATUS_NOT_FOUND; |
| 3383 | } | 3438 | } |
| 3384 | 3439 | ||
| 3440 | user = &snac; /* for L() */ | ||
| 3441 | set_user_lang(&snac); | ||
| 3442 | |||
| 3385 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) | 3443 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) |
| 3386 | proxy = 1; | 3444 | proxy = 1; |
| 3387 | 3445 | ||
| @@ -3550,7 +3608,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3550 | html_user_head(&snac, NULL, NULL), | 3608 | html_user_head(&snac, NULL, NULL), |
| 3551 | xs_html_add(html_user_body(&snac, 0), | 3609 | xs_html_add(html_user_body(&snac, 0), |
| 3552 | page, | 3610 | page, |
| 3553 | html_footer())); | 3611 | html_footer(user))); |
| 3554 | 3612 | ||
| 3555 | *body = xs_html_render_s(html, "<!DOCTYPE html>\n"); | 3613 | *body = xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3556 | *b_size = strlen(*body); | 3614 | *b_size = strlen(*body); |
| @@ -4005,6 +4063,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4005 | (void)ctype; | 4063 | (void)ctype; |
| 4006 | 4064 | ||
| 4007 | int status = 0; | 4065 | int status = 0; |
| 4066 | const snac *user = NULL; | ||
| 4008 | snac snac; | 4067 | snac snac; |
| 4009 | const char *uid; | 4068 | const char *uid; |
| 4010 | const char *p_path; | 4069 | const char *p_path; |
| @@ -4028,6 +4087,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4028 | return HTTP_STATUS_UNAUTHORIZED; | 4087 | return HTTP_STATUS_UNAUTHORIZED; |
| 4029 | } | 4088 | } |
| 4030 | 4089 | ||
| 4090 | user = &snac; /* for L() */ | ||
| 4091 | set_user_lang(&snac); | ||
| 4092 | |||
| 4031 | p_vars = xs_dict_get(req, "p_vars"); | 4093 | p_vars = xs_dict_get(req, "p_vars"); |
| 4032 | 4094 | ||
| 4033 | if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ | 4095 | if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ |
| @@ -4470,6 +4532,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4470 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); | 4532 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); |
| 4471 | else | 4533 | else |
| 4472 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); | 4534 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); |
| 4535 | if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) | ||
| 4536 | snac.config = xs_dict_set(snac.config, "lang", v); | ||
| 4473 | 4537 | ||
| 4474 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); | 4538 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); |
| 4475 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); | 4539 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); |