| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | __filename__ = "cache.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2022-02-03 13:58:20 +00:00
										 |  |  | __version__ = "1.3.0" | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							| 
									
										
										
										
											2021-09-10 16:14:50 +00:00
										 |  |  | __email__ = "bob@libreserver.org" | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | __status__ = "Production" | 
					
						
							| 
									
										
										
										
											2021-06-26 11:16:41 +00:00
										 |  |  | __module_group__ = "Core" | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2019-06-30 15:18:40 +00:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | from session import url_exists | 
					
						
							|  |  |  | from session import get_json | 
					
						
							| 
									
										
										
										
											2021-12-26 15:13:34 +00:00
										 |  |  | from utils import load_json | 
					
						
							| 
									
										
										
										
											2021-12-26 14:47:21 +00:00
										 |  |  | from utils import save_json | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | from utils import get_file_case_insensitive | 
					
						
							| 
									
										
										
										
											2021-12-26 12:24:40 +00:00
										 |  |  | from utils import get_user_paths | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 19:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  | def _remove_person_from_cache(base_dir: str, person_url: str, | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |                               person_cache: {}) -> bool: | 
					
						
							| 
									
										
										
										
											2021-02-14 13:01:52 +00:00
										 |  |  |     """Removes an actor from the cache
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     cache_filename = base_dir + '/cache/actors/' + \ | 
					
						
							|  |  |  |         person_url.replace('/', '#') + '.json' | 
					
						
							|  |  |  |     if os.path.isfile(cache_filename): | 
					
						
							| 
									
										
										
										
											2021-02-14 13:01:52 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             os.remove(cache_filename) | 
					
						
							| 
									
										
										
										
											2021-11-25 18:42:38 +00:00
										 |  |  |         except OSError: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             print('EX: unable to delete cached actor ' + str(cache_filename)) | 
					
						
							|  |  |  |     if person_cache.get(person_url): | 
					
						
							|  |  |  |         del person_cache[person_url] | 
					
						
							| 
									
										
										
										
											2021-02-14 13:01:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def check_for_changed_actor(session, base_dir: str, | 
					
						
							|  |  |  |                             http_prefix: str, domain_full: str, | 
					
						
							| 
									
										
										
										
											2022-03-02 12:01:05 +00:00
										 |  |  |                             person_url: str, avatar_url: str, person_cache: {}, | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |                             timeout_sec: int): | 
					
						
							| 
									
										
										
										
											2021-02-14 13:43:05 +00:00
										 |  |  |     """Checks if the avatar url exists and if not then
 | 
					
						
							|  |  |  |     the actor has probably changed without receiving an actor/Person Update. | 
					
						
							|  |  |  |     So clear the actor from the cache and it will be refreshed when the next | 
					
						
							|  |  |  |     post from them is sent | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-03-02 12:01:05 +00:00
										 |  |  |     if not session or not avatar_url: | 
					
						
							| 
									
										
										
										
											2021-02-14 13:43:05 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2022-03-02 12:01:05 +00:00
										 |  |  |     if domain_full in avatar_url: | 
					
						
							| 
									
										
										
										
											2021-02-14 13:43:05 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2022-03-02 12:01:05 +00:00
										 |  |  |     if url_exists(session, avatar_url, timeout_sec, http_prefix, domain_full): | 
					
						
							| 
									
										
										
										
											2021-02-14 13:43:05 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     _remove_person_from_cache(base_dir, person_url, person_cache) | 
					
						
							| 
									
										
										
										
											2021-02-14 13:43:05 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  | def store_person_in_cache(base_dir: str, person_url: str, | 
					
						
							|  |  |  |                           person_json: {}, person_cache: {}, | 
					
						
							|  |  |  |                           allow_write_to_file: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     """Store an actor in the cache
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if 'statuses' in person_url or person_url.endswith('/actor'): | 
					
						
							| 
									
										
										
										
											2020-12-12 09:54:42 +00:00
										 |  |  |         # This is not an actor or person account | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-26 13:17:46 +00:00
										 |  |  |     curr_time = datetime.datetime.utcnow() | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     person_cache[person_url] = { | 
					
						
							|  |  |  |         "actor": person_json, | 
					
						
							| 
									
										
										
										
											2021-12-26 13:17:46 +00:00
										 |  |  |         "timestamp": curr_time.strftime("%Y-%m-%dT%H:%M:%SZ") | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-12-25 16:17:53 +00:00
										 |  |  |     if not base_dir: | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # store to file | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if not allow_write_to_file: | 
					
						
							| 
									
										
										
										
											2021-06-22 11:25:28 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2021-12-25 16:17:53 +00:00
										 |  |  |     if os.path.isdir(base_dir + '/cache/actors'): | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         cache_filename = base_dir + '/cache/actors/' + \ | 
					
						
							|  |  |  |             person_url.replace('/', '#') + '.json' | 
					
						
							|  |  |  |         if not os.path.isfile(cache_filename): | 
					
						
							|  |  |  |             save_json(person_json, cache_filename) | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 16:54:44 +00:00
										 |  |  | def get_person_from_cache(base_dir: str, person_url: str, | 
					
						
							|  |  |  |                           person_cache: {}) -> {}: | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     """Get an actor from the cache
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  |     # if the actor is not in memory then try to load it from file | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     loaded_from_file = False | 
					
						
							|  |  |  |     if not person_cache.get(person_url): | 
					
						
							| 
									
										
										
										
											2020-08-29 10:21:29 +00:00
										 |  |  |         # does the person exist as a cached file? | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         cache_filename = base_dir + '/cache/actors/' + \ | 
					
						
							|  |  |  |             person_url.replace('/', '#') + '.json' | 
					
						
							|  |  |  |         actor_filename = get_file_case_insensitive(cache_filename) | 
					
						
							|  |  |  |         if actor_filename: | 
					
						
							|  |  |  |             person_json = load_json(actor_filename) | 
					
						
							|  |  |  |             if person_json: | 
					
						
							|  |  |  |                 store_person_in_cache(base_dir, person_url, person_json, | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |                                       person_cache, False) | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |                 loaded_from_file = True | 
					
						
							| 
									
										
										
										
											2020-03-22 21:16:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if person_cache.get(person_url): | 
					
						
							|  |  |  |         if not loaded_from_file: | 
					
						
							| 
									
										
										
										
											2019-08-20 09:50:27 +00:00
										 |  |  |             # update the timestamp for the last time the actor was retrieved | 
					
						
							| 
									
										
										
										
											2021-12-26 13:17:46 +00:00
										 |  |  |             curr_time = datetime.datetime.utcnow() | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             curr_time_str = curr_time.strftime("%Y-%m-%dT%H:%M:%SZ") | 
					
						
							|  |  |  |             person_cache[person_url]['timestamp'] = curr_time_str | 
					
						
							|  |  |  |         return person_cache[person_url]['actor'] | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def expire_person_cache(person_cache: {}): | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  |     """Expires old entries from the cache in memory
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-26 13:17:46 +00:00
										 |  |  |     curr_time = datetime.datetime.utcnow() | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  |     removals = [] | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     for person_url, cache_json in person_cache.items(): | 
					
						
							|  |  |  |         cache_time = datetime.datetime.strptime(cache_json['timestamp'], | 
					
						
							|  |  |  |                                                 "%Y-%m-%dT%H:%M:%SZ") | 
					
						
							|  |  |  |         days_since_cached = (curr_time - cache_time).days | 
					
						
							|  |  |  |         if days_since_cached > 2: | 
					
						
							|  |  |  |             removals.append(person_url) | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  |     if len(removals) > 0: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         for person_url in removals: | 
					
						
							|  |  |  |             del person_cache[person_url] | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  |         print(str(len(removals)) + ' actors were expired from the cache') | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-01 15:11:42 +00:00
										 |  |  | def store_webfinger_in_cache(handle: str, webfing, | 
					
						
							|  |  |  |                              cached_webfingers: {}) -> None: | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  |     """Store a webfinger endpoint in the cache
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-01-01 15:11:42 +00:00
										 |  |  |     cached_webfingers[handle] = webfing | 
					
						
							| 
									
										
										
										
											2020-04-02 09:02:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def get_webfinger_from_cache(handle: str, cached_webfingers: {}) -> {}: | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     """Get webfinger endpoint from the cache
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-25 22:28:18 +00:00
										 |  |  |     if cached_webfingers.get(handle): | 
					
						
							|  |  |  |         return cached_webfingers[handle] | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     return None | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  | def get_person_pub_key(base_dir: str, session, person_url: str, | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |                        person_cache: {}, debug: bool, | 
					
						
							|  |  |  |                        project_version: str, http_prefix: str, | 
					
						
							|  |  |  |                        domain: str, onion_domain: str, | 
					
						
							| 
									
										
										
										
											2022-03-11 13:27:54 +00:00
										 |  |  |                        i2p_domain: str, | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |                        signing_priv_key_pem: str) -> str: | 
					
						
							| 
									
										
										
										
											2022-05-27 09:20:37 +00:00
										 |  |  |     """Get the public key for an actor
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if not person_url: | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |         return None | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     person_url = person_url.replace('#main-key', '') | 
					
						
							|  |  |  |     users_paths = get_user_paths() | 
					
						
							|  |  |  |     for possible_users_path in users_paths: | 
					
						
							|  |  |  |         if person_url.endswith(possible_users_path + 'inbox'): | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 print('DEBUG: Obtaining public key for shared inbox') | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             person_url = \ | 
					
						
							|  |  |  |                 person_url.replace(possible_users_path + 'inbox', '/inbox') | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     person_json = \ | 
					
						
							| 
									
										
										
										
											2022-06-09 16:54:44 +00:00
										 |  |  |         get_person_from_cache(base_dir, person_url, person_cache) | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if not person_json: | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             print('DEBUG: Obtaining public key for ' + person_url) | 
					
						
							|  |  |  |         person_domain = domain | 
					
						
							| 
									
										
										
										
											2021-12-25 20:43:43 +00:00
										 |  |  |         if onion_domain: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             if '.onion/' in person_url: | 
					
						
							|  |  |  |                 person_domain = onion_domain | 
					
						
							| 
									
										
										
										
											2022-03-11 13:27:54 +00:00
										 |  |  |         elif i2p_domain: | 
					
						
							|  |  |  |             if '.i2p/' in person_url: | 
					
						
							|  |  |  |                 person_domain = i2p_domain | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         profile_str = 'https://www.w3.org/ns/activitystreams' | 
					
						
							|  |  |  |         accept_str = \ | 
					
						
							|  |  |  |             'application/activity+json; profile="' + profile_str + '"' | 
					
						
							|  |  |  |         as_header = { | 
					
						
							|  |  |  |             'Accept': accept_str | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         person_json = \ | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |             get_json(signing_priv_key_pem, | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |                      session, person_url, as_header, None, debug, | 
					
						
							|  |  |  |                      project_version, http_prefix, person_domain) | 
					
						
							|  |  |  |         if not person_json: | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     pub_key = None | 
					
						
							|  |  |  |     if person_json.get('publicKey'): | 
					
						
							|  |  |  |         if person_json['publicKey'].get('publicKeyPem'): | 
					
						
							|  |  |  |             pub_key = person_json['publicKey']['publicKeyPem'] | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |         if person_json.get('publicKeyPem'): | 
					
						
							|  |  |  |             pub_key = person_json['publicKeyPem'] | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     if not pub_key: | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |             print('DEBUG: Public key not found for ' + person_url) | 
					
						
							| 
									
										
										
										
											2021-07-31 11:56:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-30 18:38:36 +00:00
										 |  |  |     store_person_in_cache(base_dir, person_url, person_json, | 
					
						
							|  |  |  |                           person_cache, True) | 
					
						
							|  |  |  |     return pub_key |