// Submodules #include "http/server/request.h" #include "util/format.h" // Model #include "model/media.h" // Controller #include "controller/mastodon_api.h" #include "controller/pleroma_api.h" #include "controller/oauth.h" #include "controller/webfinger.h" #include "controller/nodeinfo.h" #include "controller/owner.h" #include "controller/inbox.h" #include "controller/activity_pub.h" #include "controller/api/emoji.h" // Standard Library #include #include const char* mime_type_for_filename( const char* filename ) { // determine MIME type int len = strlen(filename); struct { const char* extension; const char* mime_type; } extensions[] = { { ".html", "text/html" }, { ".js", "application/javascript" }, { ".png", "image/png" }, { ".jpg", "image/jpg" }, { ".css", "text/css" }, { ".json", "application/json" }, { ".svg", "image/svg+xml" }, { ".woff", "font/woff" }, { ".woff2", "font/woff2" }, { ".mp3", "audio/mp3" }, { ".ogg", "audio/ogg" }, { ".oga", "audio/ogg" }, { ".jsonld", "application/ld+json" }, { NULL, NULL }, }; for( int i = 0; extensions[i].extension; ++i ) { int s = strlen( extensions[i].extension ); if( ( len - s >= 0 ) && ( 0 == strcmp( extensions[i].extension, &filename[len-s] ) ) ) { return extensions[i].mime_type; } } return NULL; } static bool send_asset( struct http_request* req, const char* fs_path ) { const char* mime_type = mime_type_for_filename(fs_path); if( !mime_type ) { printf( "Unable to determine MIME type for %s\n", fs_path ); return false; } if( 0 == strcmp( mime_type, "application/ld+json" ) ) { http_request_add_header( req, "Access-Control-Allow-Origin", "*" ); } return http_request_send_file( req, fs_path, mime_type ); } bool route_asset( struct http_request* req ) { bool result = false; char* full_path = NULL; // Don't allow ".." in the path full_path = strdup(http_request_get_full_path( req )); if( strstr( full_path, ".." ) ) { goto failed; } // Cut off the query string char* res; strtok_r( full_path, "?", &res ); // Handle index if( full_path[ strlen(full_path)-1 ] == '/' ) { free(full_path); return send_asset( req, "assets/soapbox/index.html" ); } struct { const char* fs_path; const char* where; } fs_mounts[] = { { "assets/soapbox", "/" }, { "data/config/assets", "/" }, { NULL, NULL } }; char filename[512]; for( int i = 0; fs_mounts[i].fs_path; ++i ) { int ws = strlen( fs_mounts[i].where ); if( 0 == strncmp( fs_mounts[i].where, full_path, ws ) ) { snprintf( filename, sizeof(filename), "%s/%s", fs_mounts[i].fs_path, &full_path[ws] ); printf( "Trying %s\n", filename ); FILE* f = fopen(filename,"r"); if( f ) { fclose(f); if( send_asset( req, filename ) ) { goto success; } else { goto failed; } } } } goto failed; failed: printf( "failed to process asset: %s\n", full_path ); result = false; goto cleanup; success: result = true; goto cleanup; cleanup: free(full_path); return result; } // Route: /media/ bool route_media( struct http_request* req ) { char* id_str = http_request_route_get_dir_or_file(req); if( !id_str || !*id_str ) { return false; } // Route: /media/%d{id}/ int id = -1; if( 1 != sscanf( id_str, "%d", &id ) ) { return false; } free(id_str); struct media* m = media_from_id( id ); if( !m ) { return false; } bool subroute( struct http_request* req, struct media* m ) { char filename[512]; if( http_request_route_term( req, "blob" ) ) { return http_request_send_file( req, format(filename,sizeof(filename),"data/media/%d.blob",m->id), m->content_type ); } else if( http_request_route_term( req, "preview" ) ) { return http_request_send_file( req, format(filename,sizeof(filename),"data/media/%d.blob.preview",m->id), m->content_type ); } return false; } bool res = subroute( req, m ); media_free(m); return res; } bool route_request( struct http_request* req ) { bool inner( struct http_request* req ) { if( http_request_route( req, "/oauth" ) ) { return route_oauth( req ); } else if( http_request_route( req, "/owner" ) ) { return route_owner( req ); } else if( http_request_route( req, "/inbox" ) ) { return route_inbox( req ); } else if( http_request_route( req, "/outbox" ) ) { return route_ap_outbox( req ); } else if( http_request_route( req, "/note/" ) ) { return route_ap_note( req ); } else if( http_request_route( req, "/activity/" ) ){ return route_ap_activity(req); } else if( http_request_route( req, "/media/" ) ) { return route_media( req ); } else if( http_request_route( req, "/emoji/" ) ) { return route_emoji_asset( req ); } else if( http_request_route( req, "/.well-known" ) ) { if( http_request_route( req, "/webfinger?" ) ) { return route_wellknown_webfinger( req ); } else if( http_request_route( req, "/nodeinfo" ) ) { printf( "Nodeinfo\n" ); return route_wellknown_nodeinfo( req ); } } else if( http_request_route( req, "/nodeinfo" ) ) { return route_nodeinfo(req); } else { return route_asset(req); } return false; } if( http_request_route( req, "/api/v1/" ) ) { return route_mastodon_api( req ); } else if( http_request_route( req, "/api/pleroma" ) ) { return route_pleroma_api2( req ); } else if( inner( req ) ) { return true; } else { return send_asset( req, "assets/soapbox/index.html" ); } }