diff --git a/content.py b/content.py index 07f9885a4..807b138ef 100644 --- a/content.py +++ b/content.py @@ -234,7 +234,9 @@ def dangerous_css(filename: str, allow_local_network_access: bool) -> bool: if ctr > 0: if ')' in url_str: url_str = url_str.split(')')[0] - if 'http' in url_str: + if 'http' in url_str or \ + 'ipfs' in url_str or \ + 'ipns' in url_str: print('ERROR: non-local web link in CSS ' + filename) return True @@ -1114,6 +1116,8 @@ def get_mentions_from_html(html_text: str, match_str: str) -> []: if actor_str.startswith('http') or \ actor_str.startswith('gnunet') or \ actor_str.startswith('i2p') or \ + actor_str.startswith('ipfs') or \ + actor_str.startswith('ipns') or \ actor_str.startswith('hyper') or \ actor_str.startswith('dat:'): if actor_str not in mentions: diff --git a/daemon.py b/daemon.py index 02892de67..0e8ee384b 100644 --- a/daemon.py +++ b/daemon.py @@ -2219,7 +2219,9 @@ class PubServer(BaseHTTPRequestHandler): else: search_handle = '' if '@' not in search_handle: - if search_handle.startswith('http'): + if search_handle.startswith('http') or \ + search_handle.startswith('ipfs') or \ + search_handle.startswith('ipns'): search_nickname = \ get_nickname_from_actor(search_handle) if search_nickname: @@ -2286,6 +2288,8 @@ class PubServer(BaseHTTPRequestHandler): print('moderation_text: ' + moderation_text) nickname = moderation_text if nickname.startswith('http') or \ + nickname.startswith('ipfs') or \ + nickname.startswith('ipns') or \ nickname.startswith('hyper'): nickname = get_nickname_from_actor(nickname) if '@' in nickname: @@ -2301,6 +2305,8 @@ class PubServer(BaseHTTPRequestHandler): if moderation_button == 'block': full_block_domain = None if moderation_text.startswith('http') or \ + moderation_text.startswith('ipfs') or \ + moderation_text.startswith('ipns') or \ moderation_text.startswith('hyper'): # https://domain block_domain, block_port = \ @@ -2320,6 +2326,8 @@ class PubServer(BaseHTTPRequestHandler): if moderation_button == 'unblock': full_block_domain = None if moderation_text.startswith('http') or \ + moderation_text.startswith('ipfs') or \ + moderation_text.startswith('ipns') or \ moderation_text.startswith('hyper'): # https://domain block_domain, block_port = \ diff --git a/desktop_client.py b/desktop_client.py index acd8dc95d..49f094fb2 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -882,9 +882,12 @@ def _desktop_show_profile(session, nickname: str, domain: str, is_http = False if 'http://' in actor: is_http = True + is_gnunet = False + is_ipfs = False + is_ipns = False actor_json, _ = \ - get_actor_json(domain, actor, is_http, False, False, True, - signing_priv_key_pem, session) + get_actor_json(domain, actor, is_http, is_gnunet, is_ipfs, is_ipns, + False, True, signing_priv_key_pem, session) _desktop_show_actor(base_dir, actor_json, translate, system_language, screenreader, espeak) @@ -904,7 +907,8 @@ def _desktop_show_profile_from_handle(session, nickname: str, domain: str, Returns the actor json """ actor_json, _ = \ - get_actor_json(domain, handle, False, False, False, True, + get_actor_json(domain, handle, False, False, False, False, + False, True, signing_priv_key_pem, session) _desktop_show_actor(base_dir, actor_json, translate, diff --git a/epicyon.py b/epicyon.py index 67073ce82..b5aa6e348 100644 --- a/epicyon.py +++ b/epicyon.py @@ -503,6 +503,12 @@ parser.add_argument("--http", type=str2bool, nargs='?', parser.add_argument("--gnunet", type=str2bool, nargs='?', const=True, default=False, help="Use gnunet protocol only") +parser.add_argument("--ipfs", type=str2bool, nargs='?', + const=True, default=False, + help="Use ipfs protocol only") +parser.add_argument("--ipns", type=str2bool, nargs='?', + const=True, default=False, + help="Use ipns protocol only") parser.add_argument("--dat", type=str2bool, nargs='?', const=True, default=False, help="Use dat protocol only") @@ -736,6 +742,10 @@ if args.testsnetwork: http_prefix = 'https' if args.http or args.i2p: http_prefix = 'http' +elif args.ipfs: + http_prefix = 'ipfs' +elif args.ipns: + http_prefix = 'ipns' elif args.gnunet: http_prefix = 'gnunet' @@ -2282,6 +2292,10 @@ if args.gnunet: http_prefix = 'gnunet' if args.dat or args.hyper: http_prefix = 'hyper' +if args.ipfs: + http_prefix = 'ipfs' +if args.ipns: + http_prefix = 'ipns' if args.i2p: http_prefix = 'http' @@ -2295,6 +2309,14 @@ if args.migrations: http_prefix = 'http' port = 80 proxy_type = 'i2p' + elif args.ipfs: + http_prefix = 'ipfs' + port = 80 + proxy_type = 'ipfs' + elif args.ipns: + http_prefix = 'ipns' + port = 80 + proxy_type = 'ipfs' elif args.gnunet: http_prefix = 'gnunet' port = 80 @@ -2328,6 +2350,7 @@ if args.actor: else: print('Did not obtain instance actor key for ' + domain) get_actor_json(domain, args.actor, args.http, args.gnunet, + args.ipfs, args.ipns, debug, False, signing_priv_key_pem, None) sys.exit() @@ -2336,6 +2359,8 @@ if args.followers: if '/@' in args.followers or \ '/users/' in args.followers or \ args.followers.startswith('http') or \ + args.followers.startswith('ipfs') or \ + args.followers.startswith('ipns') or \ args.followers.startswith('hyper'): # format: https://domain/@nick prefixes = get_protocol_prefixes() @@ -2400,6 +2425,14 @@ if args.followers: http_prefix = 'gnunet' port = 80 proxy_type = 'gnunet' + elif args.ipfs: + http_prefix = 'ipfs' + port = 80 + proxy_type = 'ipfs' + elif args.ipns: + http_prefix = 'ipns' + port = 80 + proxy_type = 'ipfs' else: http_prefix = 'https' port = 443 @@ -2512,6 +2545,12 @@ if args.addaccount: domain.endswith('.i2p'): port = 80 http_prefix = 'http' + if domain.endswith('.ipfs'): + port = 80 + http_prefix = 'ipfs' + if domain.endswith('.ipns'): + port = 80 + http_prefix = 'ipns' create_person(base_dir, nickname, domain, port, http_prefix, True, not args.noapproval, args.password.strip()) if os.path.isdir(account_dir): diff --git a/follow.py b/follow.py index 29b2e3ce0..dc234e15f 100644 --- a/follow.py +++ b/follow.py @@ -414,6 +414,8 @@ def _get_no_of_follows(base_dir: str, nickname: str, domain: str, not line.startswith('http'): ctr += 1 elif ((line.startswith('http') or + line.startswith('ipfs') or + line.startswith('ipns') or line.startswith('hyper')) and has_users_path(line)): ctr += 1 @@ -542,6 +544,8 @@ def get_following_feed(base_dir: str, domain: str, port: int, path: str, url = http_prefix + '://' + dom + '/c/' + nick following['orderedItems'].append(url) elif ((line.startswith('http') or + line.startswith('ipfs') or + line.startswith('ipns') or line.startswith('hyper')) and has_users_path(line)): # https://domain/users/nickname diff --git a/migrate.py b/migrate.py index c2cf396ef..9ff4a68ff 100644 --- a/migrate.py +++ b/migrate.py @@ -87,8 +87,15 @@ def _update_moved_handle(base_dir: str, nickname: str, domain: str, gnunet = False if http_prefix == 'gnunet': gnunet = True + ipfs = False + if http_prefix == 'ipfs': + ipfs = True + ipns = False + if http_prefix == 'ipns': + ipns = True person_json = \ - get_actor_json(domain, person_url, http_prefix, gnunet, debug, False, + get_actor_json(domain, person_url, http_prefix, gnunet, ipfs, ipns, + debug, False, signing_priv_key_pem, None) if not person_json: return ctr diff --git a/newswire.py b/newswire.py index 71435c2a2..f554fe657 100644 --- a/newswire.py +++ b/newswire.py @@ -471,7 +471,7 @@ def _valid_podcast_entry(base_dir: str, key: str, entry: {}) -> bool: https://github.com/Podcastindex-org/podcast-namespace/ blob/main/proposal-docs/social/social.md#socialinteract-element """ - if key == 'socialInteract': + if key == 'socialInteract' or key == 'discussion': if not entry.get('protocol'): return False if not entry.get('uri'): @@ -515,7 +515,9 @@ def xml_podcast_to_dict(base_dir: str, xml_item: str, xml_str: str) -> {}: "transcripts": [], "valueRecipients": [], "trailers": [], - "socialInteract": [] + "discussion": [], + "episode": '', + "socialInteract": [], } pod_lines = xml_item.split(' {}: pod_val = pod_line.split('>', 1)[1].strip() if '<' in pod_val: pod_val = pod_val.split('<')[0] - podcast_properties[pod_key] = pod_val + if pod_key in podcast_properties: + podcast_properties[pod_key] = pod_val ctr += 1 continue pod_key = pod_line.split(' ')[0] diff --git a/person.py b/person.py index 924384ea0..98e6206a0 100644 --- a/person.py +++ b/person.py @@ -1478,6 +1478,7 @@ def _detect_users_path(url: str) -> str: def get_actor_json(host_domain: str, handle: str, http: bool, gnunet: bool, + ipfs: bool, ipns: bool, debug: bool, quiet: bool, signing_priv_key_pem: str, existing_session) -> ({}, {}): @@ -1493,6 +1494,8 @@ def get_actor_json(host_domain: str, handle: str, http: bool, gnunet: bool, if '/@' in handle or \ detected_users_path in handle or \ handle.startswith('http') or \ + handle.startswith('ipfs') or \ + handle.startswith('ipns') or \ handle.startswith('hyper'): group_paths = get_group_paths() if detected_users_path in group_paths: @@ -1558,6 +1561,12 @@ def get_actor_json(host_domain: str, handle: str, http: bool, gnunet: bool, elif gnunet: http_prefix = 'gnunet' proxy_type = 'gnunet' + elif ipfs: + http_prefix = 'ipfs' + proxy_type = 'ipfs' + elif ipns: + http_prefix = 'ipns' + proxy_type = 'ipfs' else: if '127.0.' not in domain and '192.168.' not in domain: http_prefix = 'https' @@ -1741,8 +1750,12 @@ def valid_sending_actor(session, base_dir: str, # NOTE: the actor should not be obtained from the local cache, # because they may have changed fields which are being tested here, # such as the bio length + gnunet = False + ipfs = False + ipns = False actor_json, _ = get_actor_json(domain, sending_actor, - True, False, debug, True, + True, gnunet, ipfs, ipns, + debug, True, signing_priv_key_pem, session) if not actor_json: # if the actor couldn't be obtained then proceed anyway diff --git a/pgp.py b/pgp.py index 5879840a6..687baea71 100644 --- a/pgp.py +++ b/pgp.py @@ -439,8 +439,8 @@ def _get_pgp_public_key_from_actor(signing_priv_key_pem: str, """ if not actor_json: actor_json, _ = \ - get_actor_json(domain, handle, False, False, False, True, - signing_priv_key_pem, None) + get_actor_json(domain, handle, False, False, False, False, + False, True, signing_priv_key_pem, None) if not actor_json: return None if not actor_json.get('attachment'): @@ -498,8 +498,8 @@ def pgp_public_key_upload(base_dir: str, session, print('Getting actor for ' + handle) actor_json, _ = \ - get_actor_json(domain_full, handle, False, False, debug, True, - signing_priv_key_pem, session) + get_actor_json(domain_full, handle, False, False, False, False, + debug, True, signing_priv_key_pem, session) if not actor_json: if debug: print('No actor returned for ' + handle) diff --git a/posts.py b/posts.py index 8f91ab740..f21f76cdb 100644 --- a/posts.py +++ b/posts.py @@ -2196,7 +2196,10 @@ def create_report_post(base_dir: str, if moderator_actor not in moderators_list: moderators_list.append(moderator_actor) continue - if line.startswith('http') or line.startswith('hyper'): + if line.startswith('http') or \ + line.startswith('ipfs') or \ + line.startswith('ipns') or \ + line.startswith('hyper'): # must be a local address - no remote moderators if '://' + domain_full + '/' in line: if line not in moderators_list: diff --git a/pyjsonld.py b/pyjsonld.py index 0d789a13c..ce4630c9d 100644 --- a/pyjsonld.py +++ b/pyjsonld.py @@ -374,7 +374,7 @@ def load_document(url): # validate URL pieces = urllib_parse.urlparse(url) if (not all([pieces.scheme, pieces.netloc]) or - pieces.scheme not in ['http', 'https', 'hyper'] or + pieces.scheme not in ['http', 'https', 'hyper', 'ipfs', 'ipns'] or set(pieces.netloc) > set( string.ascii_letters + string.digits + '-.:')): raise JsonLdError( diff --git a/session.py b/session.py index 32ffa516c..bbea835ba 100644 --- a/session.py +++ b/session.py @@ -49,6 +49,9 @@ def create_session(proxy_type: str): session.proxies = {} session.proxies['http'] = 'socks5h://localhost:7777' session.proxies['https'] = 'socks5h://localhost:7777' + elif proxy_type == 'ipfs' or proxy_type == 'ipns': + session.proxies = {} + session.proxies['ipfs'] = 'socks5h://localhost:4001' # print('New session created with proxy ' + str(proxy_type)) return session diff --git a/siteactive.py b/siteactive.py index 05e6e94f2..a19145a6b 100644 --- a/siteactive.py +++ b/siteactive.py @@ -66,7 +66,7 @@ def _site_active_parse_url(url): return loc -def _site_a_ctive_http_connect(loc, timeout: int): +def _site_active_http_connect(loc, timeout: int): """Connects to the host and returns an HTTP or HTTPS connections.""" if loc.scheme == "https": ssl_context = ssl.SSLContext() @@ -78,7 +78,7 @@ def _site_a_ctive_http_connect(loc, timeout: int): def _site_active_http_request(loc, timeout: int): """Performs a HTTP request and return response in a Result object. """ - conn = _site_a_ctive_http_connect(loc, timeout) + conn = _site_active_http_connect(loc, timeout) method = 'HEAD' conn.request(method, loc.path) @@ -98,7 +98,9 @@ def site_is_active(url: str, timeout: int) -> bool: This can be used to check that an instance is online before trying to send posts to it. """ - if not url.startswith('http'): + if not url.startswith('http') and \ + not url.startswith('ipfs') and \ + not url.startswith('ipns'): return False if '.onion/' in url or '.i2p/' in url or \ url.endswith('.onion') or \ diff --git a/tests.py b/tests.py index f0b1f7bab..d6c30c06c 100644 --- a/tests.py +++ b/tests.py @@ -6779,7 +6779,7 @@ def _test_xml_podcast_dict(base_dir: str) -> None: '' podcast_properties = xml_podcast_to_dict(base_dir, xml_str, xml_str) assert podcast_properties - # pprint(podcast_properties) + pprint(podcast_properties) assert podcast_properties.get('valueRecipients') assert podcast_properties.get('persons') assert podcast_properties.get('soundbites') diff --git a/utils.py b/utils.py index fba926fb6..3c704fd9f 100644 --- a/utils.py +++ b/utils.py @@ -654,6 +654,7 @@ def get_protocol_prefixes() -> []: """ return ('https://', 'http://', 'ftp://', 'dat://', 'i2p://', 'gnunet://', + 'ipfs://', 'ipns://', 'hyper://', 'gemini://', 'gopher://') @@ -1963,7 +1964,7 @@ def _get_reserved_words() -> str: 'ignores', 'linksmobile', 'newswiremobile', 'minimal', 'search', 'eventdelete', 'searchemoji', 'catalog', 'conversationId', - 'mention', 'http', 'https', + 'mention', 'http', 'https', 'ipfs', 'ipns', 'ontologies', 'data') diff --git a/webapp_podcast.py b/webapp_podcast.py index 0b6d8d6c7..8c829ba99 100644 --- a/webapp_podcast.py +++ b/webapp_podcast.py @@ -28,23 +28,26 @@ def _html_podcast_social_interactions(podcast_properties: {}, """ if not podcast_properties: return '' - if not podcast_properties.get('socialInteract'): - return '' - if podcast_properties['socialInteract'].get('uri'): - episode_post_url = podcast_properties['socialInteract']['uri'] - elif podcast_properties['socialInteract'].get('url'): - episode_post_url = podcast_properties['socialInteract']['url'] - elif podcast_properties['socialInteract'].get('text'): - episode_post_url = podcast_properties['socialInteract']['text'] + key = 'discussion' + if not podcast_properties.get(key): + key = 'socialInteract' + if not podcast_properties.get(key): + return '' + if podcast_properties[key].get('uri'): + episode_post_url = podcast_properties[key]['uri'] + elif podcast_properties[key].get('url'): + episode_post_url = podcast_properties[key]['url'] + elif podcast_properties[key].get('text'): + episode_post_url = podcast_properties[key]['text'] else: return '' actor_str = '' podcast_account_id = None - if podcast_properties['socialInteract'].get('accountId'): - podcast_account_id = podcast_properties['socialInteract']['accountId'] - elif podcast_properties['socialInteract'].get('podcastAccountUrl'): + if podcast_properties[key].get('accountId'): + podcast_account_id = podcast_properties[key]['accountId'] + elif podcast_properties[key].get('podcastAccountUrl'): podcast_account_id = \ - podcast_properties['socialInteract']['podcastAccountUrl'] + podcast_properties[key]['podcastAccountUrl'] if podcast_account_id: actor_handle = podcast_account_id if actor_handle.startswith('@'): diff --git a/webapp_profile.py b/webapp_profile.py index 9eeb71b7d..1d1bfb244 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -152,10 +152,16 @@ def html_profile_after_search(css_cache: {}, """ http = False gnunet = False + ipfs = False + ipns = False if http_prefix == 'http': http = True elif http_prefix == 'gnunet': gnunet = True + elif http_prefix == 'ipfs': + ipfs = True + elif http_prefix == 'ipns': + ipns = True from_domain = domain if onion_domain: if '.onion/' in profile_handle or profile_handle.endswith('.onion'): @@ -166,7 +172,8 @@ def html_profile_after_search(css_cache: {}, from_domain = i2p_domain http = True profile_json, as_header = \ - get_actor_json(from_domain, profile_handle, http, gnunet, debug, False, + get_actor_json(from_domain, profile_handle, http, + gnunet, ipfs, ipns, debug, False, signing_priv_key_pem, session) if not profile_json: return None