Correctly apply limits and since_id arguments to /api/v1/notifications, generate Accept action for Follow response (not yet signed), expand ap/activity model and add ap_activity_create_accept, add notification_new, don't include a 'status' in notifications if not set

master
teknomunk 1 year ago
parent f92860d564
commit ec7147dfff

@ -1 +1 @@
Subproject commit 0da0499f10c6e0e135eed26d7fe37800bc4a9db4
Subproject commit eb86ce14d5441bf915db91833efdee7871baad4a

@ -2,12 +2,15 @@
#include "status.h"
#include "http_server/http_request.h"
#include "fs_list.h"
#include "model/notification.h"
#include "collections/array.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void show_notifications( struct http_request* req, struct notification* ns, int count )
void show_notifications( struct http_request* req, struct notification** ns, int count )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
@ -17,7 +20,72 @@ void show_notifications( struct http_request* req, struct notification* ns, int
if( i > 0 ) {
fprintf( f, "," );
}
notification_write_as_json(&ns[i],f);
notification_write_as_json(ns[i],f);
}
fprintf( f, "]" );
}
bool handle_notifications( struct http_request* req )
{
int note_count = fs_list_get("data/notices/HEAD");
struct {
struct notification** items;
int count;
} notes;
memset(&notes,0,sizeof(notes));
int limit = 32;
bool hide_muted = true;
// Handle query parameters
if( http_request_route( req, "?" ) ) {
const char* key;
while( key = http_request_route_query_key(req) ) {
if( 0 == strcmp(key,"limit") ) {
int new_limit;
sscanf(http_request_route_query_value(req),"%d",&new_limit);
if( new_limit < limit ) {
limit = new_limit;
}
} else if( 0 == strcmp(key,"with_muted") ) {
hide_muted = false;
} else if( 0 == strcmp(key,"since_id") ) {
int since_id = 0;
sscanf(http_request_route_query_value(req),"%d",&since_id);
if( since_id <= note_count ) {
limit = note_count - since_id;
} else {
limit = 0;
}
}
}
}
for( int i = 0; (i < limit) && (i < note_count); ++i ) {
int note_id = note_count - i;
struct notification* n = notification_from_id(note_id);
bool include = true;
// Apply filters
if( !n ) { include = false; }
if( n ) {
}
if( include ) { // TODO: filter notes here
array_append( &notes, sizeof(void*), &n );
} else {
printf( "Failed to load notice #%d\n", note_id );
notification_free(n);
}
}
show_notifications( req, notes.items, notes.count );
for( int i = 0; i < notes.count; ++i ) {
notification_free(notes.items[i]);
}
free(notes.items);
return true;
}

@ -1,7 +1,10 @@
#pragma once
#include <stdbool.h>
struct http_request;
struct notification;
void show_notifications( struct http_request* req, struct notification* ns, int count );
void show_notifications( struct http_request* req, struct notification** ns, int count );
bool handle_notifications( struct http_request* req );

@ -76,6 +76,10 @@ bool route_inbox( struct http_request* req )
}
bool route_undo_activity( struct ap_activity* act )
{
if( act->object.tag != apaot_activity ) {
// Don't undo activities that are references
return false;
}
if( !act->object.ptr ) {
printf( "No object in activity\n" );
return false;
@ -124,7 +128,19 @@ bool route_follow( struct ap_activity* act )
return false;
}
printf( "TODO: %s wants to follow %s\n", act->actor, act->object.ref );
// Create Accept activity
struct ap_activity* accept = ap_activity_create_accept(act);
char filename[512]; snprintf( filename, 512, "data/outbox/%d.json", accept->local_id );
char tmp_filename[512]; snprintf( tmp_filename, 512, "%s.tmp", filename );
FILE* f = fopen(tmp_filename,"w");
fprintf( f, "to: %d\n", follower->id );
ap_activity_write_to_FILE( accept, f );
rename( tmp_filename, filename );
ap_activity_free(accept);
exit(0);
return false;
}
@ -163,14 +179,20 @@ bool process_one()
if( !env ) { return false; }
validate_signature(env);
// Discard delete requests
if( env->activity.type == apat_delete ) {
step_tail = true;
goto step;
}
if( !env->validated ) { return false; }
printf( "Processing %d\n", id );
step_tail = route_activity( &env->activity );
ap_envelope_free(env);
step:
printf( "handled: %c\n", step_tail ? 'T' : 'F' );
if( step_tail ) {
fs_list_set( "data/inbox/TAIL", id );
return true;

@ -91,13 +91,7 @@ bool route_mastodon_api( struct http_request* req )
struct account* owner = account_from_id(0);
if( http_request_route( req, "notifications" ) ) {
struct notification* note = notification_from_id(0);
if( note ) {
show_notifications( req, note, 1 );
notification_free(note);
return true;
}
return false;
return handle_notifications(req);
} else if( http_request_route( req, "filters" ) ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );

@ -1 +1 @@
Subproject commit c8df4630afa66d98a448f532ad3436843db55db1
Subproject commit 0c8f1e1d52b6dd89876c3f3c05a28c468bf68d43

@ -1,9 +1,15 @@
#define _GNU_SOURCE
#include "activity.h"
#include "json/json.h"
#include "fs_list.h"
#include "collections/array.h"
#include "model/server.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static struct json_enum ap_signature_type_enum[] = {
@ -20,8 +26,10 @@ static struct json_object_field ap_signature_layout[] = {
};
static struct json_enum ap_activity_type_enum[] = {
{ "Undo", 1 },
{ "Follow", 2 },
{ "Undo", apat_undo },
{ "Follow", apat_follow },
{ "Delete", apat_delete },
{ "Accept", apat_accept },
{ NULL, 0 },
};
@ -32,6 +40,53 @@ struct ap_activity* ap_activity_new()
return act;
}
struct ap_activity* ap_activity_dup( struct ap_activity* act )
{
struct ap_activity* new_act = ap_activity_new();
new_act->id = strdup(act->id);
new_act->local_id = act->local_id;
new_act->type = act->type;
new_act->actor = strdup(act->actor);
array_dup( &new_act->to, sizeof(char*), &act->to );
for( int i = 0; i < new_act->to.count; ++i ) {
new_act->to.items[i] = strdup(new_act->to.items[i]);
}
array_dup( &new_act->cc, sizeof(char*), &act->cc );
for( int i = 0; i < new_act->cc.count; ++i ) {
new_act->cc.items[i] = strdup(new_act->cc.items[i]);
}
array_dup( &new_act->bcc, sizeof(char*), &act->bcc );
for( int i = 0; i < new_act->bcc.count; ++i ) {
new_act->bcc.items[i] = strdup(new_act->bcc.items[i]);
}
switch( act->object.tag ) {
case apaot_ref:
new_act->object.ref = strdup(act->object.ref);
break;
case apaot_activity:
new_act->object.ptr = ap_activity_dup(act->object.ptr);
break;
}
printf( "duplicated tag = %d\n", act->object.tag );
new_act->object.tag = act->object.tag;
new_act->signature.type = act->signature.type;
if( act->signature.creator ) {
new_act->signature.creator = strdup(act->signature.creator);
};
if( act->signature.created ) {
new_act->signature.created = strdup(act->signature.created);
}
if( act->signature.value ) {
new_act->signature.value = strdup(act->signature.value);
}
return new_act;
}
static void* ap_activity_alloc()
{
return (void*)ap_activity_new();
@ -39,6 +94,7 @@ static void* ap_activity_alloc()
void ap_activity_free( struct ap_activity* act )
{
if( !act ) { return; }
printf( "ap_activity_free( %p )\n", act );
ap_activity_free_composite(act);
free(act);
}
@ -62,14 +118,19 @@ void ap_activity_free_composite( struct ap_activity* act )
}
free(act->bcc.items);
free( act->object.ref );
ap_activity_free( act->object.ptr );
switch( act->object.tag ) {
case apaot_ref:
free( act->object.ref );
break;
case apaot_activity:
ap_activity_free( act->object.ptr );
act->object.ptr = 0;
break;
};
free( act->signature.creator );
free( act->signature.created );
free( act->signature.value );
free(act);
}
static void ap_activity_free2( void* ptr )
{
@ -85,6 +146,12 @@ struct json_field_type ap_activity_type = {
.free = ap_activity_free2,
};
struct json_object_field activity_ref_types[] = {
{ (char*)apaot_ref, offsetof( struct ap_activity, object.ref ), false, &json_field_string },
{ (char*)apaot_activity, offsetof( struct ap_activity, object.ptr ), false, &json_field_object_pointer, ap_activity_layout },
{ NULL },
};
struct json_object_field ap_activity_layout[] = {
{ "id", offsetof( struct ap_activity, id ), true, &json_field_string },
{ "type", offsetof( struct ap_activity, type ), true, &json_field_enum, ap_activity_type_enum },
@ -92,8 +159,52 @@ struct json_object_field ap_activity_layout[] = {
{ "to", offsetof( struct ap_activity, to ), true, &json_field_array_of, &json_field_string },
{ "cc", offsetof( struct ap_activity, cc ), false, &json_field_array_of, &json_field_string },
{ "bcc", offsetof( struct ap_activity, bcc ), false, &json_field_array_of, &json_field_string },
{ "object", offsetof( struct ap_activity, object.ref ), false, &json_field_string },
{ "object", offsetof( struct ap_activity, object.ptr ), false, &json_field_object_pointer, ap_activity_layout },
{ "object", offsetof( struct ap_activity, object.tag ), false, &json_field_tagged_union, &activity_ref_types },
//{ "object", offsetof( struct ap_activity, object.ref ), false, &json_field_string },
//{ "object", offsetof( struct ap_activity, object.ptr ), false, &json_field_object_pointer, ap_activity_layout },
{ "signature", offsetof( struct ap_activity, signature ), false, &json_field_object_composite, ap_signature_layout },
{ NULL, 0, true, NULL, &ap_activity_type },
};
void ap_activity_write_to_FILE( struct ap_activity* act, FILE* f )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = 0,
};
json_write_pretty_object_layout( &jw, ap_activity_layout, act );
}
struct ap_activity* ap_activity_create_accept( struct ap_activity* act )
{
int id = fs_list_get("data/outbox/HEAD") + 1;
fs_list_set( "data/outbox/HEAD", id );
char* act_id; asprintf( &act_id,"https://%s/activity/%d", g_server_name, id );
char* actor; asprintf( &actor, "https://%s/owner/actor", g_server_name );
struct ap_activity* accept = malloc(sizeof(struct ap_activity));
memset(accept,0,sizeof(*accept));
accept->id = act_id;
accept->local_id = id;
accept->type = apat_accept;
accept->actor = actor;
char* new_act_actor = strdup(act->actor);
array_append( &accept->to, sizeof(char*), &new_act_actor );
printf( "accept->to = { .count = %d, .items = %p }\n", accept->to.count, accept->to.items );
printf( "...->to.items[0] = %s\n", accept->to.items[0] );
accept->object.tag = apaot_activity;
accept->object.ptr = ap_activity_dup(act);
accept->signature.type = apst_rsa_signature_2017;
accept->signature.creator = strdup(actor);
accept->signature.creator = strdup(actor);
accept->signature.created = strdup("TBD");
accept->signature.value = strdup("TBD");
return accept;
}

@ -19,11 +19,18 @@ enum ap_activity_type
{
apat_undo = 1,
apat_follow = 2,
apat_delete = 3,
apat_accept = 4,
};
enum ap_activity_object_type {
apaot_ref = 1,
apaot_activity = 2,
};
struct ap_activity
{
char* id;
int local_id;
int type;
char* actor;
struct {
@ -31,8 +38,11 @@ struct ap_activity
int count;
} to, cc, bcc;
struct {
char* ref;
struct ap_activity* ptr;
int tag;
union {
char* ref;
struct ap_activity* ptr;
};
} object;
struct ap_signature signature;
};
@ -42,6 +52,11 @@ extern struct json_field_type ap_activity_type;
extern struct json_object_field ap_activity_layout[];
struct ap_activity* ap_activity_new();
struct ap_activity* ap_activity_dup( struct ap_activity* act );
void ap_activity_free( struct ap_activity* act );
void ap_activity_free_composite( struct ap_activity* act );
void ap_activity_write_to_FILE( struct ap_activity* act, FILE* f );
struct ap_activity* ap_activity_create_accept( struct ap_activity* act );

@ -3,6 +3,7 @@
#include "json/json.h"
#include "json/layout.h"
#include "fs_list.h"
#include "model/account.h"
#include "model/status.h"
@ -10,15 +11,17 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
static struct json_enum notification_type_enum[] = {
{ "favorite", 1 },
{ "follow", 2 },
{ NULL, -1 },
};
static struct json_object_field notification_layout[] = {
{ "account_id", offsetof( struct notification, account_id ), true, &json_field_integer },
{ "status_id", offsetof( struct notification, status_id ), true, &json_field_integer },
{ "status_id", offsetof( struct notification, status_id ), false, &json_field_integer },
{ "type", offsetof( struct notification, type ), true, &json_field_enum, notification_type_enum },
{ NULL }
};
@ -26,6 +29,8 @@ static struct json_object_field notification_layout[] = {
struct notification* notification_from_id( int id )
{
struct notification* note = malloc(sizeof(struct notification));
memset(note,0,sizeof(*note));
note->id = id;
char filename[512];
snprintf( filename, 512, "data/notices/%d.json", id );
@ -37,6 +42,16 @@ struct notification* notification_from_id( int id )
return note;
}
struct notification* notification_new()
{
int id = fs_list_get( "data/notices/HEAD" ) + 1;
struct notification* note = malloc(sizeof(struct notification));
memset(note,0,sizeof(*note));
note->id = id;
fs_list_set( "data/notices/TAIL", id );
return note;
}
void notification_save( struct notification* note )
{
char filename[512];

@ -18,9 +18,11 @@ struct notification
enum notification_type
{
nt_favorite = 1,
nt_follow = 2,
};
struct notification* notification_from_id( int id );
struct notification* notification_new();
void notification_save( struct notification* note );
void notification_free( struct notification* note );

@ -6,10 +6,13 @@
"is_muted": false,
"is_seen": false
},
%( if(s) { )
"status": %( api_status_write_as_json(s,f); ),
%( } )
"type": "%(
switch(n->type) {
case nt_favorite: fprintf(f,"favourite"); break;
case nt_follow: fprintf(f,"follow"); break;
default: fprintf(f,"unknown-%d",n->type); break;
}; )"
}

Loading…
Cancel
Save