diff --git a/daemon.py b/daemon.py index edddfd25..6ff0da91 100644 --- a/daemon.py +++ b/daemon.py @@ -1377,7 +1377,72 @@ class PubServer(BaseHTTPRequestHandler): self.end_headers() self.server.GETbusy=False return - + + # get the inbox for a given person + if self.path.endswith('/dm') or '/dm?page=' in self.path: + if '/users/' in self.path: + if authorized: + inboxDMFeed=personBoxJson(self.server.baseDir, \ + self.server.domain, \ + self.server.port, \ + self.path, \ + self.server.httpPrefix, \ + maxPostsInFeed, 'dm', \ + True,self.server.ocapAlways) + if inboxDMFeed: + if self._requestHTTP(): + nickname=self.path.replace('/users/','').replace('/dm','') + pageNumber=1 + if '?page=' in nickname: + pageNumber=nickname.split('?page=')[1] + nickname=nickname.split('?page=')[0] + if pageNumber.isdigit(): + pageNumber=int(pageNumber) + else: + pageNumber=1 + if 'page=' not in self.path: + # if no page was specified then show the first + inboxDMFeed=personBoxJson(self.server.baseDir, \ + self.server.domain, \ + self.server.port, \ + self.path+'?page=1', \ + self.server.httpPrefix, \ + maxPostsInFeed, 'dm', \ + True,self.server.ocapAlways) + msg=htmlInbox(pageNumber,maxPostsInFeed, \ + self.server.session, \ + self.server.baseDir, \ + self.server.cachedWebfingers, \ + self.server.personCache, \ + nickname, \ + self.server.domain, \ + self.server.port, \ + inboxDMFeed, \ + self.server.allowDeletion, \ + self.server.httpPrefix, \ + self.server.projectVersion).encode('utf-8') + self._set_headers('text/html',len(msg),cookie) + self.wfile.write(msg) + else: + msg=json.dumps(inboxDMFeed).encode('utf-8') + self._set_headers('application/json',len(msg),None) + self.wfile.write(msg) + self.server.GETbusy=False + return + else: + if self.server.debug: + nickname=self.path.replace('/users/','').replace('/dm','') + print('DEBUG: '+nickname+ \ + ' was not authorized to access '+self.path) + if self.path!='/dm': + # not the DM inbox + if self.server.debug: + print('DEBUG: GET access to inbox is unauthorized') + self.send_response(405) + self.end_headers() + self.server.GETbusy=False + return + # get outbox feed for a person outboxFeed=personBoxJson(self.server.baseDir,self.server.domain, \ self.server.port,self.path, \ diff --git a/person.py b/person.py index 7c3e4c04..9003a5a8 100644 --- a/person.py +++ b/person.py @@ -18,6 +18,7 @@ from Crypto.PublicKey import RSA from shutil import copyfile from webfinger import createWebfingerEndpoint from webfinger import storeWebfingerEndpoint +from posts import createDMTimeline from posts import createInbox from posts import createOutbox from posts import createModeration @@ -387,7 +388,8 @@ def personBoxJson(baseDir: str,domain: str,port: int,path: str, \ authorized: bool,ocapAlways: bool) -> []: """Obtain the inbox/outbox/moderation feed for the given person """ - if boxname!='inbox' and boxname!='outbox' and boxname!='moderation': + if boxname!='inbox' and boxname!='dm' and \ + boxname!='outbox' and boxname!='moderation': return None if not '/'+boxname in path: @@ -424,6 +426,9 @@ def personBoxJson(baseDir: str,domain: str,port: int,path: str, \ if boxname=='inbox': return createInbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,ocapAlways,pageNumber) + if boxname=='dm': + return createDMTimeline(baseDir,nickname,domain,port,httpPrefix, \ + noOfItems,headerOnly,ocapAlways,pageNumber) elif boxname=='outbox': return createOutbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,authorized,pageNumber) diff --git a/posts.py b/posts.py index 968e242a..f9d5a0fd 100644 --- a/posts.py +++ b/posts.py @@ -1508,6 +1508,11 @@ def createInbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \ itemsPerPage,headerOnly,True,ocapAlways,pageNumber) +def createDMTimeline(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ + itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: + return createBoxBase(baseDir,'dm',nickname,domain,port,httpPrefix, \ + itemsPerPage,headerOnly,True,ocapAlways,pageNumber) + def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}: return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \ @@ -1581,15 +1586,41 @@ def getStatusNumberFromPostFilename(filename) -> int: return None return int(filename.split('#')[-1].replace('.json','')) +def isDM(postJsonObject: {}) -> bool: + """Returns true if the given post is a DM + """ + if postJsonObject['type']!='Create': + return False + if not postJsonObject.get('object'): + return False + if not isinstance(postJsonObject['object'], dict): + return False + if postJsonObject['object']['type']!='Note': + return False + fields=['to','cc'] + for f in fields: + if not postJsonObject['object'].get(f): + continue + for toAddress in postJsonObject['object'][f]: + if toAddress.endswith('#Public'): + return False + if toAddress.endswith('followers'): + return False + return True + def createBoxBase(baseDir: str,boxname: str, \ nickname: str,domain: str,port: int,httpPrefix: str, \ itemsPerPage: int,headerOnly: bool,authorized :bool, \ ocapAlways: bool,pageNumber=None) -> {}: """Constructs the box feed for a person with the given nickname """ - if boxname!='inbox' and boxname!='outbox': + if boxname!='inbox' and boxname!='dm' and boxname!='outbox': return None - boxDir = createPersonDir(nickname,domain,baseDir,boxname) + if boxname!='dm': + boxDir = createPersonDir(nickname,domain,baseDir,boxname) + else: + # extract DMs from the inbox + boxDir = createPersonDir(nickname,domain,baseDir,'inbox') sharedBoxDir=None if boxname=='inbox': sharedBoxDir = createPersonDir('inbox',domain,baseDir,boxname) @@ -1708,31 +1739,33 @@ def createBoxBase(baseDir: str,boxname: str, \ # get the post as json with open(filePath, 'r') as fp: p=commentjson.load(fp) - - # remove any capability so that it's not displayed - if p.get('capability'): - del p['capability'] - # Don't show likes or replies to unauthorized viewers - if not authorized: - if p.get('object'): - if isinstance(p['object'], dict): - if p['object'].get('likes'): - p['likes']={} - if p['object'].get('replies'): - p['replies']={} - # insert it into the box feed - if postsOnPageCtr < itemsPerPage: - if not headerOnly: - boxItems['orderedItems'].append(p) - postsOnPageCtr += 1 - elif postsOnPageCtr == itemsPerPage: - # if this is the last post update the next message ID - if '/statuses/' in p['id']: - postId = p['id'].split('/statuses/')[1].replace('/activity','') - boxHeader['next']= \ - httpPrefix+'://'+domain+'/users/'+ \ - nickname+'/'+boxname+'?max_id='+ \ - postId+'&page=true' + + if boxname!='dm' or \ + (boxname=='dm' and isDM(p)) + # remove any capability so that it's not displayed + if p.get('capability'): + del p['capability'] + # Don't show likes or replies to unauthorized viewers + if not authorized: + if p.get('object'): + if isinstance(p['object'], dict): + if p['object'].get('likes'): + p['likes']={} + if p['object'].get('replies'): + p['replies']={} + # insert it into the box feed + if postsOnPageCtr < itemsPerPage: + if not headerOnly: + boxItems['orderedItems'].append(p) + postsOnPageCtr += 1 + elif postsOnPageCtr == itemsPerPage: + # if this is the last post update the next message ID + if '/statuses/' in p['id']: + postId = p['id'].split('/statuses/')[1].replace('/activity','') + boxHeader['next']= \ + httpPrefix+'://'+domain+'/users/'+ \ + nickname+'/'+boxname+'?max_id='+ \ + postId+'&page=true' # remember the last post filename for use with prev prevPostFilename = postFilename if postsOnPageCtr >= itemsPerPage: diff --git a/utils.py b/utils.py index f1282bf7..1efc0d1b 100644 --- a/utils.py +++ b/utils.py @@ -271,7 +271,7 @@ def validNickname(domain: str,nickname: str) -> bool: return False if nickname==domain: return False - reservedNames=['inbox','outbox','following','followers','capabilities'] + reservedNames=['inbox','dm','outbox','following','public','followers','capabilities'] if nickname in reservedNames: return False return True