diff --git a/daemon.py b/daemon.py index e9811f752..128414500 100644 --- a/daemon.py +++ b/daemon.py @@ -10202,6 +10202,7 @@ class PubServer(BaseHTTPRequestHandler): path.endswith('/followers') or \ path.endswith('/skills') or \ path.endswith('/roles') or \ + path.endswith('/wanted') or \ path.endswith('/shares'): divertToLoginScreen = False @@ -14577,6 +14578,7 @@ class PubServer(BaseHTTPRequestHandler): self.path = self.path.replace('/tlblogs/', '/tlblogs') self.path = self.path.replace('/inbox/', '/inbox') self.path = self.path.replace('/shares/', '/shares') + self.path = self.path.replace('/wanted/', '/wanted') self.path = self.path.replace('/sharedInbox/', '/sharedInbox') if self.path == '/inbox': @@ -15021,7 +15023,9 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 15) - if self.path.endswith('/outbox') or self.path.endswith('/shares'): + if self.path.endswith('/outbox') or \ + self.path.endswith('/wanted') or \ + self.path.endswith('/shares'): if usersInPath: if authorized: self.outboxAuthenticated = True @@ -15038,6 +15042,7 @@ class PubServer(BaseHTTPRequestHandler): # check that the post is to an expected path if not (self.path.endswith('/outbox') or self.path.endswith('/inbox') or + self.path.endswith('/wanted') or self.path.endswith('/shares') or self.path.endswith('/moderationaction') or self.path == '/sharedInbox'): diff --git a/shares.py b/shares.py index ed0b9ff44..af9a88b38 100644 --- a/shares.py +++ b/shares.py @@ -737,6 +737,220 @@ def sendUndoShareViaServer(baseDir: str, session, return undoShareJson +def sendWantedViaServer(baseDir, session, + fromNickname: str, password: str, + fromDomain: str, fromPort: int, + httpPrefix: str, displayName: str, + summary: str, imageFilename: str, + itemQty: float, itemType: str, itemCategory: str, + location: str, duration: str, + cachedWebfingers: {}, personCache: {}, + debug: bool, projectVersion: str, + itemMaxPrice: str, itemCurrency: str) -> {}: + """Creates a wanted item via c2s + """ + if not session: + print('WARN: No session for sendWantedViaServer') + return 6 + + # convert $4.23 to 4.23 USD + newItemMaxPrice, newItemCurrency = getPriceFromString(itemMaxPrice) + if newItemMaxPrice != itemMaxPrice: + itemMaxPrice = newItemMaxPrice + if not itemCurrency: + if newItemCurrency != itemCurrency: + itemCurrency = newItemCurrency + + fromDomainFull = getFullDomain(fromDomain, fromPort) + + toUrl = 'https://www.w3.org/ns/activitystreams#Public' + ccUrl = httpPrefix + '://' + fromDomainFull + \ + '/users/' + fromNickname + '/followers' + + actor = httpPrefix + '://' + fromDomainFull + '/users/' + fromNickname + newShareJson = { + "@context": "https://www.w3.org/ns/activitystreams", + 'type': 'Add', + 'actor': actor, + 'target': actor + '/wanted', + 'object': { + "type": "Offer", + "displayName": displayName, + "summary": summary, + "itemQty": float(itemQty), + "itemType": itemType, + "category": itemCategory, + "location": location, + "duration": duration, + "itemPrice": itemMaxPrice, + "itemCurrency": itemCurrency, + 'to': [toUrl], + 'cc': [ccUrl] + }, + 'to': [toUrl], + 'cc': [ccUrl] + } + + handle = httpPrefix + '://' + fromDomainFull + '/@' + fromNickname + + # lookup the inbox for the To handle + wfRequest = \ + webfingerHandle(session, handle, httpPrefix, + cachedWebfingers, + fromDomain, projectVersion, debug, False) + if not wfRequest: + if debug: + print('DEBUG: share webfinger failed for ' + handle) + return 1 + if not isinstance(wfRequest, dict): + print('WARN: wanted webfinger for ' + handle + + ' did not return a dict. ' + str(wfRequest)) + return 1 + + postToBox = 'outbox' + + # get the actor inbox for the To handle + (inboxUrl, pubKeyId, pubKey, + fromPersonId, sharedInbox, + avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, + personCache, projectVersion, + httpPrefix, fromNickname, + fromDomain, postToBox, + 83653) + + if not inboxUrl: + if debug: + print('DEBUG: wanted no ' + postToBox + + ' was found for ' + handle) + return 3 + if not fromPersonId: + if debug: + print('DEBUG: wanted no actor was found for ' + handle) + return 4 + + authHeader = createBasicAuthHeader(fromNickname, password) + + if imageFilename: + headers = { + 'host': fromDomain, + 'Authorization': authHeader + } + postResult = \ + postImage(session, imageFilename, [], + inboxUrl.replace('/' + postToBox, '/wanted'), + headers) + + headers = { + 'host': fromDomain, + 'Content-type': 'application/json', + 'Authorization': authHeader + } + postResult = \ + postJson(httpPrefix, fromDomainFull, + session, newShareJson, [], inboxUrl, headers, 30, True) + if not postResult: + if debug: + print('DEBUG: POST wanted failed for c2s to ' + inboxUrl) +# return 5 + + if debug: + print('DEBUG: c2s POST wanted item success') + + return newShareJson + + +def sendUndoWantedViaServer(baseDir: str, session, + fromNickname: str, password: str, + fromDomain: str, fromPort: int, + httpPrefix: str, displayName: str, + cachedWebfingers: {}, personCache: {}, + debug: bool, projectVersion: str) -> {}: + """Undoes a wanted item via c2s + """ + if not session: + print('WARN: No session for sendUndoWantedViaServer') + return 6 + + fromDomainFull = getFullDomain(fromDomain, fromPort) + + toUrl = 'https://www.w3.org/ns/activitystreams#Public' + ccUrl = httpPrefix + '://' + fromDomainFull + \ + '/users/' + fromNickname + '/followers' + + actor = httpPrefix + '://' + fromDomainFull + '/users/' + fromNickname + undoShareJson = { + "@context": "https://www.w3.org/ns/activitystreams", + 'type': 'Remove', + 'actor': actor, + 'target': actor + '/wanted', + 'object': { + "type": "Offer", + "displayName": displayName, + 'to': [toUrl], + 'cc': [ccUrl] + }, + 'to': [toUrl], + 'cc': [ccUrl] + } + + handle = httpPrefix + '://' + fromDomainFull + '/@' + fromNickname + + # lookup the inbox for the To handle + wfRequest = \ + webfingerHandle(session, handle, httpPrefix, cachedWebfingers, + fromDomain, projectVersion, debug, False) + if not wfRequest: + if debug: + print('DEBUG: unwant webfinger failed for ' + handle) + return 1 + if not isinstance(wfRequest, dict): + print('WARN: unwant webfinger for ' + handle + + ' did not return a dict. ' + str(wfRequest)) + return 1 + + postToBox = 'outbox' + + # get the actor inbox for the To handle + (inboxUrl, pubKeyId, pubKey, + fromPersonId, sharedInbox, + avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, + personCache, projectVersion, + httpPrefix, fromNickname, + fromDomain, postToBox, + 12663) + + if not inboxUrl: + if debug: + print('DEBUG: unwant no ' + postToBox + + ' was found for ' + handle) + return 3 + if not fromPersonId: + if debug: + print('DEBUG: unwant no actor was found for ' + handle) + return 4 + + authHeader = createBasicAuthHeader(fromNickname, password) + + headers = { + 'host': fromDomain, + 'Content-type': 'application/json', + 'Authorization': authHeader + } + postResult = \ + postJson(httpPrefix, fromDomainFull, + session, undoShareJson, [], inboxUrl, + headers, 30, True) + if not postResult: + if debug: + print('DEBUG: POST unwant failed for c2s to ' + inboxUrl) +# return 5 + + if debug: + print('DEBUG: c2s POST unwant success') + + return undoShareJson + + def getSharedItemsCatalogViaServer(baseDir, session, nickname: str, password: str, domain: str, port: int, diff --git a/webapp_column_left.py b/webapp_column_left.py index 20b763824..5d67fff7a 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -404,7 +404,7 @@ def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str, if '/users/' not in path: return '' path = path.replace('/inbox', '').replace('/outbox', '') - path = path.replace('/shares', '') + path = path.replace('/shares', '').replace('/wanted', '') nickname = getNicknameFromActor(path) if not nickname: diff --git a/webapp_column_right.py b/webapp_column_right.py index 60ea44614..b296027ee 100644 --- a/webapp_column_right.py +++ b/webapp_column_right.py @@ -525,7 +525,7 @@ def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str, if '/users/' not in path: return '' path = path.replace('/inbox', '').replace('/outbox', '') - path = path.replace('/shares', '') + path = path.replace('/shares', '').replace('/wanted', '') nickname = getNicknameFromActor(path) if not nickname: diff --git a/webapp_profile.py b/webapp_profile.py index f68156ecc..be7a68158 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -521,6 +521,7 @@ def htmlProfile(rssIconAtTop: bool, rolesButton = 'button' skillsButton = 'button' sharesButton = 'button' + wantedButton = 'button' if selected == 'posts': postsButton = 'buttonselected' elif selected == 'following': @@ -533,6 +534,8 @@ def htmlProfile(rssIconAtTop: bool, skillsButton = 'buttonselected' elif selected == 'shares': sharesButton = 'buttonselected' + elif selected == 'wanted': + wantedButton = 'buttonselected' loginButton = '' followApprovalsSection = '' @@ -802,6 +805,10 @@ def htmlProfile(rssIconAtTop: bool, ' ' + \ '' + profileStr += \ + ' ' + \ + '' profileStr += logoutStr + editProfileStr profileStr += ' ' profileStr += '' @@ -1820,7 +1827,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str, """Shows the edit profile screen """ path = path.replace('/inbox', '').replace('/outbox', '') - path = path.replace('/shares', '') + path = path.replace('/shares', '').replace('/wanted', '') nickname = getNicknameFromActor(path) if not nickname: return ''