|
@@ -293,47 +293,54 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
|
|
|
snac snac;
|
|
|
|
|
|
if (user_open(&snac, login)) {
|
|
|
-
|
|
|
- if (check_password(login, passwd, xs_dict_get(snac.config, "passwd"))) {
|
|
|
-
|
|
|
- xs *code = random_str();
|
|
|
+ const char *addr = xs_or(xs_dict_get(req, "remote-addr"),
|
|
|
+ xs_dict_get(req, "x-forwarded-for"));
|
|
|
|
|
|
- xs_free(*body);
|
|
|
+ if (badlogin_check(login, addr)) {
|
|
|
+
|
|
|
+ if (check_password(login, passwd, xs_dict_get(snac.config, "passwd"))) {
|
|
|
+
|
|
|
+ xs *code = random_str();
|
|
|
|
|
|
- if (strcmp(redir, "urn:ietf:wg:oauth:2.0:oob") == 0) {
|
|
|
- *body = xs_dup(code);
|
|
|
- }
|
|
|
- else {
|
|
|
- if (xs_str_in(redir, "?") != -1)
|
|
|
- *body = xs_fmt("%s&code=%s", redir, code);
|
|
|
- else
|
|
|
- *body = xs_fmt("%s?code=%s", redir, code);
|
|
|
+ xs_free(*body);
|
|
|
|
|
|
- status = HTTP_STATUS_SEE_OTHER;
|
|
|
- }
|
|
|
+ if (strcmp(redir, "urn:ietf:wg:oauth:2.0:oob") == 0) {
|
|
|
+ *body = xs_dup(code);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (xs_str_in(redir, "?") != -1)
|
|
|
+ *body = xs_fmt("%s&code=%s", redir, code);
|
|
|
+ else
|
|
|
+ *body = xs_fmt("%s?code=%s", redir, code);
|
|
|
|
|
|
-
|
|
|
- if (!xs_is_null(state) && *state) {
|
|
|
- *body = xs_str_cat(*body, "&state=");
|
|
|
- *body = xs_str_cat(*body, state);
|
|
|
- }
|
|
|
+ status = HTTP_STATUS_SEE_OTHER;
|
|
|
+ }
|
|
|
|
|
|
- srv_log(xs_fmt("oauth x-snac-login: '%s' success, redirect to %s",
|
|
|
+
|
|
|
+ if (!xs_is_null(state) && *state) {
|
|
|
+ *body = xs_str_cat(*body, "&state=");
|
|
|
+ *body = xs_str_cat(*body, state);
|
|
|
+ }
|
|
|
+
|
|
|
+ srv_log(xs_fmt("oauth x-snac-login: '%s' success, redirect to %s",
|
|
|
login, *body));
|
|
|
|
|
|
-
|
|
|
- xs *app = app_get(cid);
|
|
|
+
|
|
|
+ xs *app = app_get(cid);
|
|
|
|
|
|
- if (app != NULL) {
|
|
|
- app = xs_dict_set(app, "uid", login);
|
|
|
- app = xs_dict_set(app, "code", code);
|
|
|
- app_add(cid, app);
|
|
|
+ if (app != NULL) {
|
|
|
+ app = xs_dict_set(app, "uid", login);
|
|
|
+ app = xs_dict_set(app, "code", code);
|
|
|
+ app_add(cid, app);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ srv_log(xs_fmt("oauth x-snac-login: error getting app %s", cid));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ srv_debug(1, xs_fmt("oauth x-snac-login: login '%s' incorrect", login));
|
|
|
+ badlogin_inc(login, addr);
|
|
|
}
|
|
|
- else
|
|
|
- srv_log(xs_fmt("oauth x-snac-login: error getting app %s", cid));
|
|
|
}
|
|
|
- else
|
|
|
- srv_debug(1, xs_fmt("oauth x-snac-login: login '%s' incorrect", login));
|
|
|
|
|
|
user_free(&snac);
|
|
|
}
|
|
@@ -474,29 +481,36 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
|
|
|
snac user;
|
|
|
|
|
|
if (user_open(&user, login)) {
|
|
|
-
|
|
|
- if (check_password(login, passwd, xs_dict_get(user.config, "passwd"))) {
|
|
|
-
|
|
|
- xs *tokid = random_str();
|
|
|
+ const char *addr = xs_or(xs_dict_get(req, "remote-addr"),
|
|
|
+ xs_dict_get(req, "x-forwarded-for"));
|
|
|
|
|
|
- srv_debug(1, xs_fmt("x-snac-new-token: "
|
|
|
+ if (badlogin_check(login, addr)) {
|
|
|
+
|
|
|
+ if (check_password(login, passwd, xs_dict_get(user.config, "passwd"))) {
|
|
|
+
|
|
|
+ xs *tokid = random_str();
|
|
|
+
|
|
|
+ srv_debug(1, xs_fmt("x-snac-new-token: "
|
|
|
"successful login for %s, new token %s", login, tokid));
|
|
|
|
|
|
- xs *token = xs_dict_new();
|
|
|
- token = xs_dict_append(token, "token", tokid);
|
|
|
- token = xs_dict_append(token, "client_id", "snac-client");
|
|
|
- token = xs_dict_append(token, "client_secret", "");
|
|
|
- token = xs_dict_append(token, "uid", login);
|
|
|
- token = xs_dict_append(token, "code", "");
|
|
|
+ xs *token = xs_dict_new();
|
|
|
+ token = xs_dict_append(token, "token", tokid);
|
|
|
+ token = xs_dict_append(token, "client_id", "snac-client");
|
|
|
+ token = xs_dict_append(token, "client_secret", "");
|
|
|
+ token = xs_dict_append(token, "uid", login);
|
|
|
+ token = xs_dict_append(token, "code", "");
|
|
|
|
|
|
- token_add(tokid, token);
|
|
|
+ token_add(tokid, token);
|
|
|
|
|
|
- *ctype = "text/plain";
|
|
|
- xs_free(*body);
|
|
|
- *body = xs_dup(tokid);
|
|
|
- }
|
|
|
+ *ctype = "text/plain";
|
|
|
+ xs_free(*body);
|
|
|
+ *body = xs_dup(tokid);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ badlogin_inc(login, addr);
|
|
|
|
|
|
- user_free(&user);
|
|
|
+ user_free(&user);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -898,7 +912,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
|
|
|
const char *o_href = xs_dict_get(v, "href");
|
|
|
const char *name = xs_dict_get(v, "name");
|
|
|
|
|
|
- if (xs_match(type, "image/*|video/*|Image|Video")) {
|
|
|
+ if (xs_match(type, "image/*|video/*|audio/*|Image|Video")) {
|
|
|
xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt));
|
|
|
xs *href = make_url(o_href, proxy, 1);
|
|
|
|
|
@@ -910,7 +924,8 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
|
|
|
d = xs_dict_append(d, "remote_url", href);
|
|
|
d = xs_dict_append(d, "description", name);
|
|
|
|
|
|
- d = xs_dict_append(d, "type", (*type == 'v' || *type == 'V') ? "video" : "image");
|
|
|
+ d = xs_dict_append(d, "type", (*type == 'v' || *type == 'V') ? "video" :
|
|
|
+ (*type == 'a' || *type == 'A') ? "audio" : "image");
|
|
|
|
|
|
matt = xs_list_append(matt, d);
|
|
|
}
|
|
@@ -990,7 +1005,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
|
|
|
const char *o_url = xs_dict_get(icon, "url");
|
|
|
|
|
|
if (!xs_is_null(o_url)) {
|
|
|
- xs *url = make_url(o_url, snac->actor, 1);
|
|
|
+ xs *url = make_url(o_url, snac ? snac->actor : NULL, 1);
|
|
|
xs *nm = xs_strip_chars_i(xs_dup(name), ":");
|
|
|
|
|
|
d1 = xs_dict_append(d1, "shortcode", nm);
|
|
@@ -1193,10 +1208,13 @@ int process_auth_token(snac *snac, const xs_dict *req)
|
|
|
return logged_in;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
void credentials_get(char **body, char **ctype, int *status, snac snac)
|
|
|
{
|
|
|
xs *acct = xs_dict_new();
|
|
|
|
|
|
+ const xs_val *bot = xs_dict_get(snac.config, "bot");
|
|
|
+
|
|
|
acct = xs_dict_append(acct, "id", snac.md5);
|
|
|
acct = xs_dict_append(acct, "username", xs_dict_get(snac.config, "uid"));
|
|
|
acct = xs_dict_append(acct, "acct", xs_dict_get(snac.config, "uid"));
|
|
@@ -1206,7 +1224,7 @@ void credentials_get(char **body, char **ctype, int *status, snac snac)
|
|
|
acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio"));
|
|
|
acct = xs_dict_append(acct, "url", snac.actor);
|
|
|
acct = xs_dict_append(acct, "locked", xs_stock(XSTYPE_FALSE));
|
|
|
- acct = xs_dict_append(acct, "bot", xs_dict_get(snac.config, "bot"));
|
|
|
+ acct = xs_dict_append(acct, "bot", xs_stock(xs_is_true(bot) ? XSTYPE_TRUE : XSTYPE_FALSE));
|
|
|
acct = xs_dict_append(acct, "emojis", xs_stock(XSTYPE_LIST));
|
|
|
|
|
|
xs *src = xs_json_loads("{\"privacy\":\"public\", \"language\":\"en\","
|
|
@@ -1220,7 +1238,7 @@ void credentials_get(char **body, char **ctype, int *status, snac snac)
|
|
|
src = xs_dict_set(src, "sensitive",
|
|
|
strcmp(cw, "open") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
|
|
|
|
|
|
- src = xs_dict_set(src, "bot", xs_dict_get(snac.config, "bot"));
|
|
|
+ src = xs_dict_set(src, "bot", xs_stock(xs_is_true(bot) ? XSTYPE_TRUE : XSTYPE_FALSE));
|
|
|
|
|
|
xs *avatar = NULL;
|
|
|
const char *av = xs_dict_get(snac.config, "avatar");
|
|
@@ -1319,7 +1337,7 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn
|
|
|
|
|
|
const char *max_id = xs_dict_get(args, "max_id");
|
|
|
const char *since_id = xs_dict_get(args, "since_id");
|
|
|
- const char *min_id = xs_dict_get(args, "min_id");
|
|
|
+ const char *min_id = xs_dict_get(args, "min_id");
|
|
|
const char *limit_s = xs_dict_get(args, "limit");
|
|
|
int limit = 0;
|
|
|
int cnt = 0;
|
|
@@ -1330,7 +1348,7 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn
|
|
|
if (limit == 0)
|
|
|
limit = 20;
|
|
|
|
|
|
- if (index_desc_first(f, md5, 0)) {
|
|
|
+ if (min_id == NULL && index_desc_first(f, md5, 0)) {
|
|
|
do {
|
|
|
xs *msg = NULL;
|
|
|
|
|
@@ -1348,13 +1366,6 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- if (min_id) {
|
|
|
- if (strcmp(md5, MID_TO_MD5(min_id)) == 0)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
if (user) {
|
|
|
if (!valid_status(timeline_get_by_md5(user, md5, &msg)))
|
|
@@ -1438,8 +1449,35 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn
|
|
|
}
|
|
|
|
|
|
|
|
|
+xs_str *timeline_link_header(const char *endpoint, xs_list *timeline)
|
|
|
+
|
|
|
+{
|
|
|
+ xs_str *s = NULL;
|
|
|
+
|
|
|
+ if (xs_list_len(timeline) == 0)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ const xs_dict *first_st = xs_list_get(timeline, 0);
|
|
|
+ const xs_dict *last_st = xs_list_get(timeline, -1);
|
|
|
+ const char *first_id = xs_dict_get(first_st, "id");
|
|
|
+ const char *last_id = xs_dict_get(last_st, "id");
|
|
|
+ const char *host = xs_dict_get(srv_config, "host");
|
|
|
+ const char *protocol = xs_dict_get_def(srv_config, "protocol", "https");
|
|
|
+
|
|
|
+ s = xs_fmt(
|
|
|
+ "<%s:/" "/%s%s?max_id=%s>; rel=\"next\", "
|
|
|
+ "<%s:/" "/%s%s?since_id=%s>; rel=\"prev\"",
|
|
|
+ protocol, host, endpoint, last_id,
|
|
|
+ protocol, host, endpoint, first_id);
|
|
|
+
|
|
|
+ srv_debug(1, xs_fmt("timeline_link_header %s", s));
|
|
|
+
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
- char **body, int *b_size, char **ctype)
|
|
|
+ char **body, int *b_size, char **ctype, xs_str **link)
|
|
|
{
|
|
|
(void)b_size;
|
|
|
|
|
@@ -1699,6 +1737,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
xs *ifn = user_index_fn(&snac1, "private");
|
|
|
xs *out = mastoapi_timeline(&snac1, args, ifn);
|
|
|
|
|
|
+ *link = timeline_link_header("/api/v1/timelines/home", out);
|
|
|
+
|
|
|
*body = xs_json_dumps(out, 4);
|
|
|
*ctype = "application/json";
|
|
|
status = HTTP_STATUS_OK;
|
|
@@ -1763,8 +1803,14 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
xs *out = xs_list_new();
|
|
|
const xs_dict *v;
|
|
|
const xs_list *excl = xs_dict_get(args, "exclude_types[]");
|
|
|
+ const char *min_id = xs_dict_get(args, "min_id");
|
|
|
const char *max_id = xs_dict_get(args, "max_id");
|
|
|
|
|
|
+ if (dbglevel) {
|
|
|
+ xs *js = xs_json_dumps(args, 0);
|
|
|
+ srv_debug(1, xs_fmt("mastoapi_notifications args %s", js));
|
|
|
+ }
|
|
|
+
|
|
|
xs_list_foreach(l, v) {
|
|
|
xs *noti = notify_get(&snac1, v);
|
|
|
|
|
@@ -1795,6 +1841,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ if (min_id) {
|
|
|
+ if (strcmp(fid, min_id) <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0)
|
|
|
type = "favourite";
|
|
@@ -1842,6 +1894,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
out = xs_list_append(out, mn);
|
|
|
}
|
|
|
|
|
|
+ srv_debug(1, xs_fmt("mastoapi_notifications count %d", xs_list_len(out)));
|
|
|
+
|
|
|
*body = xs_json_dumps(out, 4);
|
|
|
*ctype = "application/json";
|
|
|
status = HTTP_STATUS_OK;
|
|
@@ -2273,9 +2327,22 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
}
|
|
|
else
|
|
|
if (strcmp(cmd, "/v1/markers") == 0) {
|
|
|
- *body = xs_dup("{}");
|
|
|
- *ctype = "application/json";
|
|
|
- status = HTTP_STATUS_OK;
|
|
|
+ if (logged_in) {
|
|
|
+ const xs_list *timeline = xs_dict_get(args, "timeline[]");
|
|
|
+ xs_str *json = NULL;
|
|
|
+ if (!xs_is_null(timeline))
|
|
|
+ json = xs_json_dumps(markers_get(&snac1, timeline), 4);
|
|
|
+
|
|
|
+ if (!xs_is_null(json))
|
|
|
+ *body = json;
|
|
|
+ else
|
|
|
+ *body = xs_dup("{}");
|
|
|
+
|
|
|
+ *ctype = "application/json";
|
|
|
+ status = HTTP_STATUS_OK;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ status = HTTP_STATUS_UNAUTHORIZED;
|
|
|
}
|
|
|
else
|
|
|
if (strcmp(cmd, "/v1/followed_tags") == 0) {
|
|
@@ -2310,6 +2377,37 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
|
|
|
if (xs_is_null(offset) || strcmp(offset, "0") == 0) {
|
|
|
|
|
|
apps like Tusky keep asking again and again */
|
|
|
+ if (xs_startswith(q, "https://")) {
|
|
|
+ xs *md5 = xs_md5_hex(q, strlen(q));
|
|
|
+
|
|
|
+ if (!timeline_here(&snac1, md5)) {
|
|
|
+ xs *object = NULL;
|
|
|
+ int status;
|
|
|
+
|
|
|
+ status = activitypub_request(&snac1, q, &object);
|
|
|
+ snac_debug(&snac1, 1, xs_fmt("Request searched URL %s %d", q, status));
|
|
|
+
|
|
|
+ if (valid_status(status)) {
|
|
|
+
|
|
|
+ const char *attr_to = get_atto(object);
|
|
|
+ xs *actor_obj = NULL;
|
|
|
+
|
|
|
+ if (!xs_is_null(attr_to)) {
|
|
|
+ status = actor_request(&snac1, attr_to, &actor_obj);
|
|
|
+
|
|
|
+ snac_debug(&snac1, 1, xs_fmt("Request author %s of %s %d", attr_to, q, status));
|
|
|
+
|
|
|
+ if (valid_status(status)) {
|
|
|
+
|
|
|
+ actor_add(attr_to, actor_obj);
|
|
|
+
|
|
|
+
|
|
|
+ timeline_add(&snac1, q, object);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (!xs_is_null(q)) {
|
|
|
if (xs_is_null(type) || strcmp(type, "accounts") == 0) {
|
|
@@ -2945,6 +3043,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
|
|
|
status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
|
|
|
}
|
|
|
}
|
|
|
+ else
|
|
|
if (xs_startswith(cmd, "/v1/lists/")) {
|
|
|
if (logged_in) {
|
|
|
xs *l = xs_split(cmd, "/");
|
|
@@ -2972,9 +3071,35 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+ else if (strcmp(cmd, "/v1/markers") == 0) {
|
|
|
+ xs_str *json = NULL;
|
|
|
+ if (logged_in) {
|
|
|
+ const xs_str *home_marker = xs_dict_get(args, "home[last_read_id]");
|
|
|
+ if (xs_is_null(home_marker)) {
|
|
|
+ const xs_dict *home = xs_dict_get(args, "home");
|
|
|
+ if (!xs_is_null(home))
|
|
|
+ home_marker = xs_dict_get(home, "last_read_id");
|
|
|
+ }
|
|
|
+
|
|
|
+ const xs_str *notify_marker = xs_dict_get(args, "notifications[last_read_id]");
|
|
|
+ if (xs_is_null(notify_marker)) {
|
|
|
+ const xs_dict *notify = xs_dict_get(args, "notifications");
|
|
|
+ if (!xs_is_null(notify))
|
|
|
+ notify_marker = xs_dict_get(notify, "last_read_id");
|
|
|
+ }
|
|
|
+ json = xs_json_dumps(markers_set(&snac, home_marker, notify_marker), 4);
|
|
|
+ }
|
|
|
+ if (!xs_is_null(json))
|
|
|
+ *body = json;
|
|
|
else
|
|
|
- status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
|
|
|
+ *body = xs_dup("{}");
|
|
|
+
|
|
|
+ *ctype = "application/json";
|
|
|
+ status = HTTP_STATUS_OK;
|
|
|
}
|
|
|
+ else
|
|
|
+ status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
|
|
|
|
|
|
|
|
|
if (logged_in)
|