Beispiel #1
0
    def _rescanName(self):
        if (self.__readonly): return
        req = urllib2.Request(r'https://api.github.com/user',
                              headers=self.__headers)
        try:
            code, rep = urlopen_nt(req)
        except urllib2.URLError as e:
            raise CollectionSyncError('unable to reach collection: %s' %
                                      e.reason)
        if (code != 200):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        data = json.loads(rep.read())
        self.__name = data['login']
Beispiel #2
0
    def addItem(self,
                desiredName,
                description,
                content,
                access=CollectionItem.AccessType.private,
                metadata=None):
        assert isinstance(desiredName, str) or isinstance(
            desiredName, unicode), 'name should be a string'
        assert isinstance(content, str) or isinstance(
            content, unicode), 'conetnt shoud be a string'
        assert access == 0 or access == 1, 'wrong access type'  #TODO there's no other type enforcement for this const for now

        if (self.__readonly):
            raise CollectionReadonlyError('collection is opened as read-only!')

        if ('nettype' not in metadata):
            raise CollectionItemInvalidError(
                'required metadata must be present in metadata')

        description = ":".join((metadata['nettype'], description))
        postdata = {
            'public': access == CollectionItem.AccessType.public,
            'description': description
        }
        postdata['files'] = {
            '00_HPASTE_SNIPPET': {
                'content': 'snippets marker'
            }
        }
        postdata['files'][desiredName] = {'content': content}
        req = urllib2.Request(r'https://api.github.com/gists',
                              json.dumps(postdata),
                              headers=self.__headers)
        code, rep = urlopen_nt(req)
        if (code != 201):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        gist = json.loads(rep.read())
        newfilenames = gist['files'].keys()
        newfilenames.remove('00_HPASTE_SNIPPET')
        if (len(newfilenames) != 1):
            raise CollectionInconsistentError(
                'something went wrong during item creating')
        newfilename = newfilenames[0]
        if (metadata is None): metadata = {}
        metadata['raw_url'] = gist['files'][newfilename]['raw_url']
        desc = gist['description']
        nettype = ''
        if (':' in desc):
            nettype, desc = desc.split(':', 1)
        metadata['nettype'] = nettype
        retaccess = CollectionItem.AccessType.public if gist[
            'public'] else CollectionItem.AccessType.private
        newitem = CollectionItem(self, newfilename, desc,
                                 '%s@%s' % (gist['id'], newfilename),
                                 retaccess, False, metadata)
        return newitem
Beispiel #3
0
    def updateItemIfNeeded(self, item):
        ver = tuple(item.metadata().get('ver', (1, 0)))
        if ver < currentVersion:  # TODO: put it into separate method
            log("upgrading collection item version to %s" %
                '.'.join(map(lambda x: str(x), currentVersion)))
            # if it's bigger but was still loadable - we don't change anything
            # For now we only know how to update 1.0 to 1.1
            if currentVersion == (
                    1, 1
            ):  # just in case i up the version and forget to change updater
                # we know exactly what's missing, so we just fix things, no checkings required
                id, filename = item.id().split('@', 1)
                data = {
                    'files': {
                        'ver:%s' % '.'.join(
                            map(lambda x: str(x), currentVersion)): {
                            'content': '==='
                        }
                    }
                }
                # ver 1.0 does not have ver: file, so we don't delete anything
                data['files'][filename] = {'filename': 'item:' + filename}

                req = urllib2.Request('https://api.github.com/gists/%s' % id,
                                      json.dumps(data),
                                      headers=self.__headers)
                req.get_method = lambda: 'PATCH'
                code, rep = urlopen_nt(req)
                if (code != 200):
                    if (code == 403): raise InvalidToken('github auth failed')
                    raise CollectionSyncError(
                        "unexpected server return level %d" % code)
                gist = json.loads(rep.read())

                itemfileanmes = [
                    x for x in gist['files'].keys() if x.startswith("item:")
                ]
                if len(itemfileanmes) != 1:
                    raise CollectionInconsistentError(
                        'something went wrong during item version update: could not find unique item data in gist'
                    )
                newfilename = itemfileanmes[0]
                newname = newfilename.split(':', 1)[1]
                desc = gist['description']
                nettype = ''
                if (':' in desc):
                    nettype, desc = desc.split(':', 1)
                item._desc = desc
                item._name = newname
                item._meta['raw_url'] = gist['files'][newfilename]['raw_url']
                item._meta['nettype'] = nettype
                item._id = '%s@%s' % (gist['id'], newfilename)
                item._access = CollectionItem.AccessType.public if gist[
                    'public'] else CollectionItem.AccessType.private
                item._readonly = False
            else:
                raise NotImplemented(
                    "version upgrade to %s is not implemented!" %
                    '.'.join(map(lambda x: str(x), currentVersion)))
Beispiel #4
0
    def list(self):
        #this should produce list of snippets in the collection
        #the list should be a tuple of CollectionItem-s
        # note, that id is not a wid,
        requrl = r'https://api.github.com/gists'
        if (self.__readonly):
            requrl = r'https://api.github.com/users/%s/gists' % self.__name
        req = urllib2.Request(requrl, headers=self.__headers)
        code, rep = urlopen_nt(req)

        if (code != 200):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        log(str(rep.info()), 0)
        data = json.loads(rep.read())
        gists = [x for x in data if '00_HPASTE_SNIPPET' in x['files']]
        if (len(gists) == 0):
            return ()

        res = []
        for gist in gists:
            files = gist['files']
            try:
                del files['00_HPASTE_SNIPPET']
            except KeyError:
                raise CollectionInconsistentError(
                    'impossible error! marker lost')
            if (len(files) != 1):
                raise CollectionInconsistentError(
                    'each gist must have one marker and one file')
            desc = gist['description']
            nettype = ''
            if (':' in desc):
                nettype, desc = desc.split(':', 1)

            for filename in files:
                filedata = files[filename]
                rawurl = filedata['raw_url']
                retaccess = CollectionItem.AccessType.public if gist[
                    'public'] else CollectionItem.AccessType.private
                newitem = CollectionItem(self,
                                         filename,
                                         desc,
                                         '%s@%s' % (gist['id'], filename),
                                         retaccess,
                                         self.__readonly,
                                         metadata={
                                             'raw_url': rawurl,
                                             'nettype': nettype
                                         })
                res.append(newitem)

        return tuple(res)
Beispiel #5
0
    def getContent(self, item):
        #this should bring the raw content of the collection item.
        assert isinstance(item,
                          CollectionItem), 'item must be a collection item'

        req = urllib2.Request(item.metadata()['raw_url'],
                              headers=self.__headers)
        try:
            code, rep = urlopen_nt(req)
        except urllib2.URLError as e:
            raise CollectionSyncError('unable to reach collection: %s' %
                                      e.reason)
        if (code != 200):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        data = rep.read()

        return data
Beispiel #6
0
def urlopen_nt(req):
    code = -1
    rep = None
    try:
        rep = urllib2.urlopen(req)
    except urllib2.HTTPError as e:
        code = e.code
    except urllib2.URLError as e:
        raise CollectionSyncError('unable to reach collection: %s' % e.reason)

    if (code == -1): code = rep.getcode()
    return code, rep
Beispiel #7
0
    def removeItem(self, item):
        assert isinstance(item,
                          CollectionItem), 'item must be a collection item'
        if (item.readonly()): raise CollectionReadonlyError()

        id, name = item.id().split('@', 1)

        req = urllib2.Request(r'https://api.github.com/gists/%s' % id,
                              headers=self.__headers)
        req.get_method = lambda: 'DELETE'
        try:
            code, rep = urlopen_nt(req)
        except urllib2.URLError as e:
            raise CollectionSyncError('unable to reach collection: %s' %
                                      e.reason)
        if (code != 204):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        item.invalidate()
Beispiel #8
0
    def changeItem(self,
                   item,
                   newName=None,
                   newDescription=None,
                   newContent=None,
                   newAccess=None):
        assert isinstance(item,
                          CollectionItem), 'item must be a collection item'
        #newName=str(newName)
        #newDescription=str(newDescription)
        #newContent=str(newContent)
        #just in case we have random unicode coming in
        #TODO: check that item belongs to this collection. just in case
        if (item.readonly()): raise CollectionReadonlyError()
        if (newAccess is not None and newAccess != item.access()):
            #raise NotImplementedError('not yet implemented')

            newitem = self.addItem(
                item.name() if newName is None else newName,
                item.description()
                if newDescription is None else newDescription,
                item.content() if newContent is None else newContent,
                newAccess, item.metadata())
            self.removeItem(
                copy.copy(item)
            )  # remove the copy cuz item gets invalidated and we dont want that for original item
            item._name = newitem._name
            item._desc = newitem._desc
            item._meta['raw_url'] = newitem._meta['raw_url']
            item._meta['nettype'] = newitem._meta['nettype']
            item._id = newitem._id
            item._access = newitem._access
            item._readonly = newitem._readonly

            #TODO: if access is changed - we have to destroy this gist and create a new one with proper 'public' key
            # Butt Beware - we need to modify item's contents and return it WITHOUT reassigning the item itself

        if ('nettype' not in item.metadata()):
            item._invalidate()
            raise CollectionItemInvalidError(
                'required metadata was not found in the item')

        id, name = item.id().split('@', 1)
        data = {}
        proceed = False
        if (newName is not None):
            data['filename'] = newName
            proceed = True
        if (newContent is not None):
            data['content'] = newContent
            proceed = True
        if (data != {}):
            data = {'files': {item.name(): data}}

        if (newDescription is not None):
            data['description'] = ':'.join(
                (item.metadata()['nettype'], newDescription))
            proceed = True

        if (not proceed): return

        req = urllib2.Request('https://api.github.com/gists/%s' % id,
                              json.dumps(data),
                              headers=self.__headers)
        req.get_method = lambda: 'PATCH'
        code, rep = urlopen_nt(req)
        if (code != 200):
            if (code == 403): raise InvalidToken('github auth failed')
            raise CollectionSyncError("unexpected server return level %d" %
                                      code)

        gist = json.loads(rep.read())
        newfilenames = gist['files'].keys()
        newfilenames.remove('00_HPASTE_SNIPPET')
        if (len(newfilenames) != 1):
            raise CollectionInconsistentError(
                'something went wrong during item changing')
        newfilename = newfilenames[0]
        desc = gist['description']
        nettype = ''
        if (':' in desc):
            nettype, desc = desc.split(':', 1)
        item._desc = desc
        item._name = newfilename
        item._meta['raw_url'] = gist['files'][newfilename]['raw_url']
        item._meta['nettype'] = nettype
        item._id = '%s@%s' % (gist['id'], newfilename)
        item._access = CollectionItem.AccessType.public if gist[
            'public'] else CollectionItem.AccessType.private
        item._readonly = False
Beispiel #9
0
    def changeItem(self,
                   item,
                   newName=None,
                   newDescription=None,
                   newContent=None,
                   newAccess=None,
                   metadataChanges=None):
        assert isinstance(item,
                          CollectionItem), 'item must be a collection item'
        #newName=str(newName)
        #newDescription=str(newDescription)
        #newContent=str(newContent)
        #just in case we have random unicode coming in
        #TODO: check that item belongs to this collection. just in case

        if item.readonly(): raise CollectionReadonlyError()

        # Upgrade version if needed
        self.updateItemIfNeeded(item)

        if newAccess is not None and newAccess != item.access():
            newitem = self.addItem(
                item.name() if newName is None else newName,
                item.description()
                if newDescription is None else newDescription,
                item.content() if newContent is None else newContent,
                newAccess, item.metadata())
            # all auxiliary files MUST BE COPIED before deleting original item
            # icon:
            id, filename = newitem.id().split('@', 1)
            if 'iconpixmap' in item.metadata(
            ) and 'iconfullname' in item.metadata(
            ) and 'icondata' in item.metadata():
                data = {
                    'files': {
                        item.metadata()['iconfullname']: {
                            'content': item.metadata()['icondata']
                        }
                    }
                }
                req = urllib2.Request('https://api.github.com/gists/%s' % id,
                                      json.dumps(data),
                                      headers=self.__headers)
                req.get_method = lambda: 'PATCH'
                code, rep = urlopen_nt(req)
                if (code != 200):
                    if (code == 403): raise InvalidToken('github auth failed')
                    raise CollectionSyncError(
                        "unexpected server return level %d" % code)

            self.removeItem(
                copy.copy(item)
            )  # remove the copy cuz item gets invalidated and we dont want that for original item
            item._name = newitem._name
            item._desc = newitem._desc
            item._meta['raw_url'] = newitem._meta['raw_url']
            item._meta['nettype'] = newitem._meta['nettype']
            item._id = newitem._id
            item._access = newitem._access
            item._readonly = newitem._readonly

            # if access is changed - we have to destroy this gist and create a new one with proper 'public' key
            # Butt Beware - we need to modify item's contents and return it WITHOUT reassigning the item itself
            # That's what we are doing here currently

            return

        if 'nettype' not in item.metadata():
            item.invalidate()
            raise CollectionItemInvalidError(
                'required metadata was not found in the item')

        id, filename = item.id().split('@', 1)
        data = {}
        proceed = False
        if (newName is not None):
            if filename.startswith(
                    'item:'
            ):  # new format. can cause trouble if someone actually called item starting with 'item:'
                data['filename'] = 'item:' + newName
            else:  # old format
                data['filename'] = newName
            proceed = True
        if (newContent is not None):
            data['content'] = newContent
            proceed = True
        if (data != {}):
            data = {'files': {filename: data}}

        if (newDescription is not None):
            data['description'] = ':'.join(
                (item.metadata()['nettype'], newDescription))
            proceed = True

        if proceed:
            req = urllib2.Request('https://api.github.com/gists/%s' % id,
                                  json.dumps(data),
                                  headers=self.__headers)
            req.get_method = lambda: 'PATCH'
            code, rep = urlopen_nt(req)
            if (code != 200):
                if (code == 403): raise InvalidToken('github auth failed')
                raise CollectionSyncError("unexpected server return level %d" %
                                          code)

            gist = json.loads(rep.read())
            newfilenames = gist['files'].keys()
            newfilenames.remove('00_HPASTE_SNIPPET')
            if (len(newfilenames) == 1):
                newfilename = newfilenames[0]
                newname = newfilename
            else:
                itemfileanmes = [
                    x for x in newfilenames if x.startswith("item:")
                ]
                if len(itemfileanmes) != 1:
                    raise CollectionInconsistentError(
                        'something went wrong during item creation: could not find unique item data in gist'
                    )
                newfilename = itemfileanmes[0]
                newname = newfilename.split(':', 1)[1]
            desc = gist['description']
            nettype = ''
            if (':' in desc):
                nettype, desc = desc.split(':', 1)
            item._desc = desc
            item._name = newname
            item._meta['raw_url'] = gist['files'][newfilename]['raw_url']
            item._meta['nettype'] = nettype
            item._id = '%s@%s' % (gist['id'], newfilename)
            item._access = CollectionItem.AccessType.public if gist[
                'public'] else CollectionItem.AccessType.private
            item._readonly = False

        # metadata changes processing
        if metadataChanges:
            metaspecialkeys = [
                'raw_url', 'nettype', 'icondata', 'iconfullname', 'iconpixmap',
                'iconfullname', 'icondata'
            ]
            #if 'icondata' in metadataChanges and ('iconfullname' in item.metadata() or 'iconfullname' in metadataChanges):
            # Shall i implement this case? for when qt is not loaded
            if 'iconpixmap' in metadataChanges and qtAvailable:
                pix = metadataChanges['iconpixmap']
                if pix is None:  # removing pixmap
                    if 'iconpixmap' in item.metadata():
                        data = {
                            'files': {
                                item.metadata()['iconfullname']: None
                            }
                        }
                        req = urllib2.Request(
                            'https://api.github.com/gists/%s' %
                            item.id().split('@', 1)[0],
                            json.dumps(data),
                            headers=self.__headers)
                        req.get_method = lambda: 'PATCH'
                        code, rep = urlopen_nt(req)
                        if (code != 200):
                            if (code == 403):
                                raise InvalidToken('github auth failed')
                            raise CollectionSyncError(
                                "unexpected server return level %d" % code)
                        rep.close()
                        del item._meta['iconpixmap']
                        del item._meta['iconfullname']
                        del item._meta['icondata']
                else:
                    barr = QByteArray()
                    buff = QBuffer(barr)
                    pix.save(buff, "PNG")
                    imagedata = base64.b64encode(barr.data())
                    buff.deleteLater()

                    oldiconname = item.metadata().get('iconfullname', None)
                    newiconname = 'icon:PNG-base64:autoicon'

                    data = {'files': {}}
                    if oldiconname is not None and oldiconname != newiconname:
                        data['files'][oldiconname] = None

                    data['files'][newiconname] = {'content': imagedata}
                    req = urllib2.Request('https://api.github.com/gists/%s' %
                                          item.id().split('@', 1)[0],
                                          json.dumps(data),
                                          headers=self.__headers)
                    req.get_method = lambda: 'PATCH'
                    code, rep = urlopen_nt(req)
                    if (code != 200):
                        if (code == 403):
                            raise InvalidToken('github auth failed')
                        raise CollectionSyncError(
                            "unexpected server return level %d" % code)
                    replydict = json.loads(rep.read())

                    if newiconname not in replydict['files']:
                        raise CollectionSyncError(
                            "icon file was not uploaded properly")

                    globalIconCacher[replydict['files'][newiconname]
                                     ['raw_url']] = imagedata
                    item._meta['iconfullname'] = newiconname
                    item._meta['icondata'] = imagedata
                    item._meta['iconpixmap'] = pix

            for metakey in metadataChanges.keys():
                # All special cases are taken care of, so now just blind copy all remaining changes
                if metakey in metaspecialkeys: continue
                item._meta[metakey] = metadataChanges[metakey]
Beispiel #10
0
    def list(self):
        #this should produce list of snippets in the collection
        #the list should be a tuple of CollectionItem-s
        # note, that id is not a wid,
        requrl = r'https://api.github.com/gists?per_page=100'
        if (self.__readonly):
            requrl = r'https://api.github.com/users/%s/gists?per_page=100' % self.name(
            )
        gists = []
        pagenum = 0
        while True:
            req = urllib2.Request(requrl, headers=self.__headers)
            code, rep = urlopen_nt(req)

            repheaders = rep.info()
            log(str(repheaders), 0)

            if (code != 200):
                if (code == 403):
                    if pagenum == 0:
                        raise InvalidToken('github auth failed')
                    else:  # means we have already succesfully got page 0 therefore 403 means limit hit or abuse trigger so we warn and continue
                        log('Not all snippets could be retrieved from github due to api rate limitation'
                            )
                        break  # TODO: postpone this and retry later!
                raise CollectionSyncError("unexpected server return level %d" %
                                          code)

            data = json.loads(rep.read())
            gists += [x for x in data if '00_HPASTE_SNIPPET' in x['files']]

            if 'link' in repheaders:
                rhlinks = repheaders['link']
                linksdict = {}
                for l in rhlinks.split(','):
                    _key = None
                    _val = None
                    for k in l.split(';'):
                        m = re.match('rel="(\w+)"', k.strip())
                        if m:
                            _key = m.group(1)
                        m = re.match('<([-a-zA-Z0-9@:%_\+.~#?&\/=]+)>',
                                     k.strip())
                        if m:
                            _val = m.group(1)
                    if _key is None or _val is None:
                        log('failed to parse page links', 3)

                    linksdict[_key] = _val
                if 'next' not in linksdict:
                    break
                requrl = linksdict['next']
                pagenum += 1
                continue
            break

        if (len(gists) == 0):
            return ()

        res = []
        for gist in gists:
            files = gist['files']
            try:
                del files['00_HPASTE_SNIPPET']
            except KeyError:
                raise CollectionInconsistentError(
                    'impossible error! marker lost')
            #if(len(files)!=1):raise CollectionInconsistentError('each gist must have one marker and one file')
            desc = gist['description']
            nettype = ''
            if (':' in desc):
                nettype, desc = desc.split(':', 1)

            if (len(files) == 1):  # zero version implementation
                for filename in files:
                    filedata = files[filename]
                    rawurl = filedata['raw_url']
                    retaccess = CollectionItem.AccessType.public if gist[
                        'public'] else CollectionItem.AccessType.private
                    newitem = CollectionItem(self,
                                             filename,
                                             desc,
                                             '%s@%s' % (gist['id'], filename),
                                             retaccess,
                                             self.__readonly,
                                             metadata={
                                                 'raw_url': rawurl,
                                                 'nettype': nettype,
                                                 'ver': (1, 0)
                                             })
                    res.append(newitem)
            else:
                # first of all check version
                verfiles = [x for x in files if x.startswith("ver:")]
                if len(verfiles) != 1:
                    log(
                        "skipping a broken collection item, id:%s" %
                        gist['id'], 2)
                    continue
                ver = map(lambda x: int(x), verfiles[0].split(':',
                                                              1)[1].split('.'))
                if ver[0] != currentVersion[0]:
                    log(
                        "unsupported collection item version, id:%s" %
                        gist['id'], 2)
                    continue

                # now get the item
                itemfiles = [x for x in files if x.startswith("item:")]
                if len(itemfiles) != 1:
                    raise CollectionInconsistentError(
                        'could not find unique item data in gist')
                filedata = files[itemfiles[0]]
                itemname = itemfiles[0].split(':', 1)[1]
                rawurl = filedata['raw_url']
                retaccess = CollectionItem.AccessType.public if gist[
                    'public'] else CollectionItem.AccessType.private
                newitem = CollectionItem(self,
                                         itemname,
                                         desc,
                                         '%s@%s' % (gist['id'], itemfiles[0]),
                                         retaccess,
                                         self.__readonly,
                                         metadata={
                                             'raw_url': rawurl,
                                             'nettype': nettype,
                                             'ver': ver
                                         })

                for typefilename in files:
                    if ':' not in typefilename: continue
                    filetype, filename = typefilename.split(':', 1)
                    if filetype == 'item':
                        continue  # Skip item file, it was already processed
                    if filetype == 'icon':
                        if not qtAvailable:
                            continue  # If we were loaded from non-interactive session - just skip icons

                        if ':' not in filename: continue
                        icontype, iconname = filename.split(':', 1)
                        if icontype not in ['XPM-simple', 'PNG-base64']:
                            continue  # supported icon format list

                        filedata = files[typefilename]
                        url = filedata['raw_url']
                        if url in globalIconCacher:
                            data = globalIconCacher[url]
                        else:
                            req = urllib2.Request(url, headers=self.__headers)
                            code, rep = urlopen_nt(req)
                            if (code != 200):
                                if code == 403:
                                    raise InvalidToken('github auth failed')
                                raise CollectionSyncError(
                                    "unexpected server return level %d" % code)
                            data = rep.read()

                        if icontype == 'XPM-simple':
                            newitem.metadata()['iconfullname'] = ':'.join(
                                (filetype, icontype, iconname))
                            newitem.metadata()['icondata'] = data
                            #xpmlines = map(lambda x: x.replace('"', ''), re.findall('"[^"]*"', data))
                            newitem.metadata()['iconpixmap'] = (
                                data, "XPM"
                            )  # Since this can be run in a different Thread - we do not create actual QPixmap here, just keep data for it's creation on demand

                        if icontype == 'PNG-base64':
                            newitem.metadata()['iconfullname'] = ':'.join(
                                (filetype, icontype, iconname))
                            newitem.metadata()['icondata'] = data
                            newitem.metadata()['iconpixmap'] = (
                                base64.b64decode(data), "PNG"
                            )  # Since this can be run in a different Thread - we do not create actual QPixmap here, just keep data for it's creation on demand

                res.append(newitem)

        return tuple(res)