11import 'package:flutter/material.dart' ;
22import 'package:html/dom.dart' as dom;
33
4+ import '../api/core.dart' ;
45import '../api/model/model.dart' ;
56import '../model/content.dart' ;
67import '../model/store.dart' ;
@@ -184,7 +185,7 @@ class MessageImage extends StatelessWidget {
184185 final src = node.srcUrl;
185186
186187 final store = PerAccountStoreWidget .of (context);
187- final adjustedSrc = rewriteImageUrl (src, store.account);
188+ final resolvedSrc = resolveUrl (src, store.account);
188189
189190 return Align (
190191 alignment: Alignment .centerLeft,
@@ -200,8 +201,11 @@ class MessageImage extends StatelessWidget {
200201 alignment: Alignment .center,
201202 color: const Color .fromRGBO (0 , 0 , 0 , 0.03 ),
202203 child: Image .network (
203- adjustedSrc ,
204+ resolvedSrc ,
204205 filterQuality: FilterQuality .medium,
206+ headers: isUrlOnRealm (resolvedSrc, store.account)
207+ ? Map .fromEntries ([authHeader (store.account)])
208+ : null ,
205209 ))));
206210 }
207211}
@@ -442,7 +446,7 @@ class MessageImageEmoji extends StatelessWidget {
442446 @override
443447 Widget build (BuildContext context) {
444448 final store = PerAccountStoreWidget .of (context);
445- final adjustedSrc = rewriteImageUrl (node.src, store.account);
449+ final resolvedSrc = resolveUrl (node.src, store.account);
446450
447451 const size = 20.0 ;
448452
@@ -456,7 +460,10 @@ class MessageImageEmoji extends StatelessWidget {
456460 // too low.
457461 top: - 1.5 ,
458462 child: Image .network (
459- adjustedSrc.toString (),
463+ resolvedSrc.toString (),
464+ headers: isUrlOnRealm (resolvedSrc, store.account)
465+ ? Map .fromEntries ([authHeader (store.account)])
466+ : null ,
460467 filterQuality: FilterQuality .medium,
461468 width: size,
462469 height: size,
@@ -469,32 +476,18 @@ class MessageImageEmoji extends StatelessWidget {
469476// Small helpers.
470477//
471478
472- /// Resolve URL if relative; add the user's API key if appropriate.
473- ///
474- /// The API key is added if the URL is on the realm, and is an endpoint
475- /// known to require authentication (and to accept it in this form.)
476- String rewriteImageUrl (String src, Account account) {
479+ /// Resolve `src` to `account` 's realm, if relative
480+ String resolveUrl (String url, Account account) {
477481 final realmUrl = Uri .parse (account.realmUrl); // TODO clean this up
478- final resolved = realmUrl.resolve (src); // TODO handle if fails to parse
479-
480- Uri adjustedSrc = resolved;
481- if (resolved.origin == realmUrl.origin) {
482- if (_kInlineApiRoutes.any ((regexp) => regexp.hasMatch (resolved.path))) {
483- final delimiter = resolved.query.isNotEmpty ? '&' : '' ;
484- adjustedSrc = resolved
485- .resolve ('?${resolved .query }${delimiter }api_key=${account .apiKey }' );
486- }
487- }
488-
489- return adjustedSrc.toString ();
482+ final resolved = realmUrl.resolve (url); // TODO handle if fails to parse
483+ return resolved.toString ();
490484}
491485
492- /// List of routes which accept the API key appended as a GET parameter.
493- final List <RegExp > _kInlineApiRoutes = [
494- RegExp (r'^/user_uploads/' ),
495- RegExp (r'^/thumbnail$' ),
496- RegExp (r'^/avatar/' )
497- ];
486+ /// Whether the given absolute URL has the same origin as the account's realm.
487+ // TODO: Take a `Uri` instead of String for `url`; remove "absolute" from doc
488+ bool isUrlOnRealm (String url, Account account) {
489+ return Uri .parse (url).origin == Uri .parse (account.realmUrl).origin;
490+ }
498491
499492InlineSpan _errorUnimplemented (UnimplementedNode node) {
500493 // For now this shows error-styled HTML code even in release mode,
0 commit comments