├── lib ├── __init__.py ├── evernote │ ├── __init__.py │ └── edam │ │ ├── __init__.py │ │ ├── error │ │ ├── __init__.py │ │ ├── constants.py │ │ └── ttypes.py │ │ ├── type │ │ ├── __init__.py │ │ └── constants.py │ │ ├── limits │ │ ├── __init__.py │ │ ├── ttypes.py │ │ └── constants.py │ │ ├── notestore │ │ ├── __init__.py │ │ ├── constants.py │ │ └── NoteStore-remote │ │ └── userstore │ │ ├── __init__.py │ │ ├── constants.py │ │ └── UserStore-remote ├── NOTICE ├── thrift │ ├── __init__.py │ ├── server │ │ ├── __init__.py │ │ ├── THttpServer.py │ │ ├── TProcessPoolServer.py │ │ ├── TServer.py │ │ └── TNonblockingServer.py │ ├── protocol │ │ ├── __init__.py │ │ ├── TBase.py │ │ ├── TBinaryProtocol.py │ │ ├── TProtocol.py │ │ └── TCompactProtocol.py │ ├── transport │ │ ├── __init__.py │ │ ├── THttpClient.py │ │ ├── TSocket.py │ │ ├── TTwisted.py │ │ ├── TSSLSocket.py │ │ ├── TZlibTransport.py │ │ └── TTransport.py │ ├── TSCons.py │ ├── TSerialization.py │ └── Thrift.py ├── LICENSE └── APACHE-LICENSE-2.0.txt ├── requirements.txt ├── .gitignore ├── credentials.py ├── diffbotHelper.py ├── sanitize.py ├── evernoteHelper.py ├── pinboardHelper.py ├── README.md ├── main.py └── LICENSE.txt /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/evernote/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/evernote/edam/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytidylib==0.1.2 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | tags 4 | venv/ 5 | -------------------------------------------------------------------------------- /lib/evernote/edam/error/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /lib/evernote/edam/type/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /lib/evernote/edam/limits/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /lib/evernote/edam/notestore/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'NoteStore'] 2 | -------------------------------------------------------------------------------- /lib/evernote/edam/userstore/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'UserStore'] 2 | -------------------------------------------------------------------------------- /lib/NOTICE: -------------------------------------------------------------------------------- 1 | Apache Thrift 2 | Copyright 2006-2010 The Apache Software Foundation. 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). -------------------------------------------------------------------------------- /lib/evernote/edam/error/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | -------------------------------------------------------------------------------- /lib/evernote/edam/notestore/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | -------------------------------------------------------------------------------- /lib/evernote/edam/userstore/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_VERSION_MAJOR = 1 13 | EDAM_VERSION_MINOR = 22 14 | -------------------------------------------------------------------------------- /lib/evernote/edam/type/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_NOTE_SOURCE_WEB_CLIP = "web.clip" 13 | EDAM_NOTE_SOURCE_MAIL_CLIP = "mail.clip" 14 | EDAM_NOTE_SOURCE_MAIL_SMTP_GATEWAY = "mail.smtp" 15 | -------------------------------------------------------------------------------- /lib/evernote/edam/limits/ttypes.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | 11 | from thrift.transport import TTransport 12 | from thrift.protocol import TBinaryProtocol, TProtocol 13 | try: 14 | from thrift.protocol import fastbinary 15 | except: 16 | fastbinary = None 17 | 18 | 19 | -------------------------------------------------------------------------------- /credentials.py: -------------------------------------------------------------------------------- 1 | # You can find your Pinboard API token on your https://pinboard.in/settings/password page. 2 | # Finally, the next line should look like: 3 | # PinboardAPIToken = "username:HEXADECIMALSTRING" 4 | PinboardAPIToken = "" 5 | 6 | # You can get a free Diffbot Developer Token from http://www.diffbot.com/plans/free. 7 | # Finally, the next line should look like: 8 | # DiffbotToken = "hexadecimalstring" 9 | DiffbotToken = "" 10 | 11 | # You can get a developer token that allows you to access your own Evernote account. 12 | # To get a developer token, visit 13 | # https://sandbox.evernote.com/api/DeveloperToken.action 14 | # OR 15 | # https://www.evernote.com/api/DeveloperToken.action 16 | EvernoteDeveloperToken = "" 17 | # Evernote Developer Tokens expire after one year from the date of their creation. So set a reminder to update the developer token after an year. 18 | -------------------------------------------------------------------------------- /lib/thrift/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['Thrift', 'TSCons'] 21 | -------------------------------------------------------------------------------- /lib/thrift/server/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TServer', 'TNonblockingServer'] 21 | -------------------------------------------------------------------------------- /lib/thrift/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TProtocol', 'TBinaryProtocol', 'fastbinary', 'TBase'] 21 | -------------------------------------------------------------------------------- /lib/thrift/transport/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TTransport', 'TSocket', 'THttpClient','TZlibTransport'] 21 | -------------------------------------------------------------------------------- /diffbotHelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib 3 | import urllib2 4 | import json 5 | 6 | 7 | def extractArticle(token, url, html=True): 8 | """ 9 | Takes the Diffbot developer token and the url of the article to be extracted. 10 | Returns the text of the article. 11 | """ 12 | if not token: 13 | print "Please fill in your diffbot developer token" 14 | print "You can get a free token from http://www.diffbot.com/plans/free" 15 | exit(1) 16 | 17 | url = urllib.quote(url) 18 | if html: 19 | request = "http://www.diffbot.com/api/article?token=%s&url=%s&html" % (token, url) 20 | responseJSON = urllib2.urlopen(request) 21 | responseDict = json.load(responseJSON) 22 | try: 23 | return responseDict["html"] 24 | except KeyError: 25 | return "" 26 | else: 27 | request = "http://www.diffbot.com/api/article?token=%s&url=%s" % (token, url) 28 | responseJSON = urllib2.urlopen(request) 29 | responseDict = json.load(responseJSON) 30 | try: 31 | return responseDict["text"] 32 | except KeyError: 33 | return "" 34 | -------------------------------------------------------------------------------- /lib/thrift/TSCons.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from os import path 21 | from SCons.Builder import Builder 22 | 23 | def scons_env(env, add=''): 24 | opath = path.dirname(path.abspath('$TARGET')) 25 | lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE' 26 | cppbuild = Builder(action = lstr) 27 | env.Append(BUILDERS = {'ThriftCpp' : cppbuild}) 28 | 29 | def gen_cpp(env, dir, file): 30 | scons_env(env) 31 | suffixes = ['_types.h', '_types.cpp'] 32 | targets = map(lambda s: 'gen-cpp/' + file + s, suffixes) 33 | return env.ThriftCpp(targets, dir+file+'.thrift') 34 | -------------------------------------------------------------------------------- /lib/thrift/TSerialization.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from protocol import TBinaryProtocol 21 | from transport import TTransport 22 | 23 | def serialize(thrift_object, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()): 24 | transport = TTransport.TMemoryBuffer() 25 | protocol = protocol_factory.getProtocol(transport) 26 | thrift_object.write(protocol) 27 | return transport.getvalue() 28 | 29 | def deserialize(base, buf, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()): 30 | transport = TTransport.TMemoryBuffer(buf) 31 | protocol = protocol_factory.getProtocol(transport) 32 | base.read(protocol) 33 | return base 34 | 35 | -------------------------------------------------------------------------------- /lib/LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007-2012 by Evernote Corporation, All rights reserved. 3 | * 4 | * Use of the source code and binary libraries included in this package 5 | * is permitted under the following terms: 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | -------------------------------------------------------------------------------- /sanitize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | reload(sys) 4 | sys.setdefaultencoding("utf-8") 5 | import re 6 | from tidylib import * 7 | from xml.dom.minidom import * 8 | 9 | 10 | def sanitize(html): 11 | document, errors = tidy_document(html, options={"output-xhtml": 1, "char-encoding":"utf8", "drop-proprietary-attributes": 1, "merge-divs": 1, "clean": 1}) # xml.dom.minidom is an XML parser, not an HTML parser. Therefore, it doesn't know any HTML entities (only those which are common to both XML and HTML). So, if I didn't give output-xhtml I got xml.parsers.expat.ExpatError: undefined entity. 12 | parsedDOM = xml.dom.minidom.parseString(document) 13 | documentElement = parsedDOM.documentElement 14 | removeProhibitedElements(documentElement) 15 | removeProhibitedAttributes(documentElement) 16 | body = documentElement.getElementsByTagName("body")[0] 17 | body.tagName = "en-note" 18 | return body.toxml() 19 | 20 | 21 | def removeProhibitedElements(documentElement): 22 | prohibitedTagNames = ["applet", "base", "basefont", "bgsound", "blink", "button", "dir", "embed", "fieldset", "form", "frame", "frameset", "head", "iframe", "ilayer", "input", "isindex", "label", "layer", "legend", "link", "marquee", "menu", "meta", "noframes", "noscript", "object", "optgroup", "option", "param", "plaintext", "script", "select", "style", "textarea", "xml", ] 23 | for tagName in prohibitedTagNames: 24 | removeProhibitedElement(tagName, documentElement) 25 | 26 | 27 | def removeProhibitedElement(tagName, documentElement): 28 | elements = documentElement.getElementsByTagName(tagName) 29 | for element in elements: 30 | p = element.parentNode 31 | p.removeChild(element) 32 | 33 | 34 | def removeProhibitedAttributes(element): 35 | prohibitedAttributes = ["id", "class", "onclick", "ondblclick", "onload", "accesskey", "data", "dynsrc", "tabindex", "onmouseover", "onmouseout", "onblur", ] 36 | #FIXME All on* attributes are prohibited. How to use a regular expression as argument to removeAttribute? 37 | for attribute in prohibitedAttributes: 38 | try: 39 | element.removeAttribute(attribute) 40 | except xml.dom.NotFoundErr: 41 | pass 42 | try: 43 | if element.hasAttribute("href"): 44 | t = element.toxml() 45 | if re.search('href="http', t) or re.search('href="https', t): 46 | pass 47 | else: 48 | element.removeAttribute("href") 49 | except: 50 | pass 51 | 52 | listOfChildren = element.childNodes 53 | for child in listOfChildren: 54 | if child.nodeType == 1: 55 | removeProhibitedAttributes(child) 56 | -------------------------------------------------------------------------------- /lib/thrift/protocol/TBase.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from thrift.Thrift import * 21 | from thrift.protocol import TBinaryProtocol 22 | from thrift.transport import TTransport 23 | 24 | try: 25 | from thrift.protocol import fastbinary 26 | except: 27 | fastbinary = None 28 | 29 | class TBase(object): 30 | __slots__ = [] 31 | 32 | def __repr__(self): 33 | L = ['%s=%r' % (key, getattr(self, key)) 34 | for key in self.__slots__ ] 35 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 36 | 37 | def __eq__(self, other): 38 | if not isinstance(other, self.__class__): 39 | return False 40 | for attr in self.__slots__: 41 | my_val = getattr(self, attr) 42 | other_val = getattr(other, attr) 43 | if my_val != other_val: 44 | return False 45 | return True 46 | 47 | def __ne__(self, other): 48 | return not (self == other) 49 | 50 | def read(self, iprot): 51 | if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: 52 | fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) 53 | return 54 | iprot.readStruct(self, self.thrift_spec) 55 | 56 | def write(self, oprot): 57 | if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: 58 | oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) 59 | return 60 | oprot.writeStruct(self, self.thrift_spec) 61 | 62 | class TExceptionBase(Exception): 63 | # old style class so python2.4 can raise exceptions derived from this 64 | # This can't inherit from TBase because of that limitation. 65 | __slots__ = [] 66 | 67 | __repr__ = TBase.__repr__.im_func 68 | __eq__ = TBase.__eq__.im_func 69 | __ne__ = TBase.__ne__.im_func 70 | read = TBase.read.im_func 71 | write = TBase.write.im_func 72 | 73 | -------------------------------------------------------------------------------- /evernoteHelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import os 5 | import time 6 | 7 | home = os.path.dirname(os.path.abspath(__file__)) 8 | evernoteLibraryPath = os.path.join(home, "lib") 9 | sys.path.insert(0, evernoteLibraryPath) 10 | 11 | import thrift.protocol.TBinaryProtocol as TBinaryProtocol 12 | import thrift.transport.THttpClient as THttpClient 13 | import evernote.edam.userstore.UserStore as UserStore 14 | import evernote.edam.userstore.constants as UserStoreConstants 15 | import evernote.edam.notestore.NoteStore as NoteStore 16 | import evernote.edam.type.ttypes as Types 17 | import evernote.edam.error.ttypes as Errors 18 | 19 | 20 | class EvernoteHelper: 21 | 22 | def __init__(self, authToken, production): 23 | if not authToken: 24 | print "Please fill in your evernote developer token" 25 | print "To get a developer token, visit" 26 | print "https://sandbox.evernote.com/api/DeveloperToken.action (for testing and development)" 27 | print "OR" 28 | print "https://www.evernote.com/api/DeveloperToken.action (for production)" 29 | exit(1) 30 | 31 | if production: 32 | self.evernoteHost = "www.evernote.com" 33 | else: 34 | self.evernoteHost = "sandbox.evernote.com" 35 | 36 | self.authToken = authToken 37 | self.userStoreUri = "https://" + self.evernoteHost + "/edam/user" 38 | self.userStoreHttpClient = THttpClient.THttpClient(self.userStoreUri) 39 | self.userStoreProtocol = TBinaryProtocol.TBinaryProtocol(self.userStoreHttpClient) 40 | self.userStore = UserStore.Client(self.userStoreProtocol) 41 | 42 | self.noteStoreUrl = self.userStore.getNoteStoreUrl(authToken) 43 | self.noteStoreHttpClient = THttpClient.THttpClient(self.noteStoreUrl) 44 | self.noteStoreProtocol = TBinaryProtocol.TBinaryProtocol(self.noteStoreHttpClient) 45 | self.noteStore = NoteStore.Client(self.noteStoreProtocol) 46 | 47 | def sendToEvernote(self, title, sourceURL, enml, notebookName=None): 48 | 49 | # To create a new note, simply create a new Note object and fill in attributes 50 | note = Types.Note() 51 | note.title = title.encode('utf-8', 'xmlcharrefreplace') 52 | note.attributes = Types.NoteAttributes() 53 | note.attributes.sourceURL = sourceURL 54 | 55 | if notebookName: 56 | notebooks = self.noteStore.listNotebooks(self.authToken) 57 | for notebook in notebooks: 58 | if notebook.name == notebookName: 59 | note.notebookGuid = notebook.guid 60 | print "Creating a new note in '%s' notebook..." % notebookName 61 | break 62 | else: 63 | print "'%s' notebook not found. Creating a new note in the default notebook instead..." % notebookName 64 | else: 65 | print "Creating a new note in the default notebook..." 66 | 67 | note.content = '' 68 | note.content += '' 69 | note.content += enml.encode('utf-8', 'xmlcharrefreplace') 70 | 71 | # Finally, send the new note to Evernote using the createNote method 72 | # The new Note object that is returned will contain server-generated 73 | # attributes such as the new note's unique GUID. 74 | createdNote = self.noteStore.createNote(self.authToken, note) 75 | 76 | print "Successfully created a new note with GUID: ", createdNote.guid 77 | print 78 | -------------------------------------------------------------------------------- /pinboardHelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import urllib2 3 | import datetime 4 | from xml.dom.minidom import * 5 | 6 | 7 | def getRecentBookmarks(auth_token): 8 | """ 9 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING) 10 | Returns a list of (href, title, extended) tuples for recent bookmarks. 11 | """ 12 | checkToken(auth_token) 13 | url = "https://api.pinboard.in/v1/posts/recent?auth_token=%s" % (auth_token) 14 | return getList(url) 15 | 16 | 17 | def getAllBookmarks(auth_token): 18 | """ 19 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING) 20 | Returns a list of (href, title, extended) tuples for all bookmarks. 21 | """ 22 | checkToken(auth_token) 23 | url = "https://api.pinboard.in/v1/posts/all?auth_token=%s" % (auth_token) 24 | return getList(url) 25 | 26 | 27 | def getAllBookmarksTagged(auth_token, tag): 28 | """ 29 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING) and a string tag 30 | Returns a list of (href, title, extended) tuples for all bookmarks which are tagged with that tag. 31 | """ 32 | checkToken(auth_token) 33 | url = "https://api.pinboard.in/v1/posts/all?auth_token=%s&tag=%s" % (auth_token, tag) 34 | return getList(url) 35 | 36 | 37 | def getTodaysBookmarks(auth_token): 38 | """ 39 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING) 40 | Returns a list of (href, title, extended) tuples for all bookmarks created today. 41 | """ 42 | checkToken(auth_token) 43 | url = "https://api.pinboard.in/v1/posts/get?auth_token=%s&dt=%s" % (auth_token, str(datetime.datetime.utcnow().date())) 44 | return getList(url) 45 | 46 | 47 | def getBookmarksFromDate(auth_token, fromdt): 48 | """ 49 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING) and a datetime.date() object. 50 | Returns a list of (href, title, extended) tuples for all bookmarks created after fromdt. 51 | """ 52 | checkToken(auth_token) 53 | url = "https://api.pinboard.in/v1/posts/all?auth_token=%s&fromdt=%s" % (auth_token, str(fromdt)) 54 | return getList(url) 55 | 56 | 57 | def getBookmarksFromDateTagged(auth_token, fromdt, tag): 58 | """ 59 | Takes the Pinboard API Token (which is of the form username:HEXADECIMALSTRING), a datetime.date() object and a string tag. 60 | Returns a list of (href, title, extended) tuples for all bookmarks created after fromdt which are tagged with that tag. 61 | """ 62 | checkToken(auth_token) 63 | url = "https://api.pinboard.in/v1/posts/all?auth_token=%s&fromdt=%s&tag=%s" % (auth_token, str(fromdt), tag) 64 | return getList(url) 65 | 66 | 67 | def checkToken(auth_token): 68 | if not auth_token: 69 | print "Please fill in your pinboard api token" 70 | print "You can find your token on your https://pinboard.in/settings/password page" 71 | exit(1) 72 | 73 | 74 | def getList(url): 75 | rawXML = urllib2.urlopen(url) 76 | parsedXML = xml.dom.minidom.parse(rawXML) 77 | bookmarkList = [] 78 | for post in parsedXML.getElementsByTagName("post"): 79 | href = post.getAttribute("href").strip() 80 | title = post.getAttribute("description").strip() 81 | extended = post.getAttribute("extended").strip() 82 | bookmark = (href, title, extended) 83 | bookmarkList.append(bookmark) 84 | return bookmarkList 85 | -------------------------------------------------------------------------------- /lib/thrift/server/THttpServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import BaseHTTPServer 21 | 22 | from thrift.server import TServer 23 | from thrift.transport import TTransport 24 | 25 | class ResponseException(Exception): 26 | """Allows handlers to override the HTTP response 27 | 28 | Normally, THttpServer always sends a 200 response. If a handler wants 29 | to override this behavior (e.g., to simulate a misconfigured or 30 | overloaded web server during testing), it can raise a ResponseException. 31 | The function passed to the constructor will be called with the 32 | RequestHandler as its only argument. 33 | """ 34 | def __init__(self, handler): 35 | self.handler = handler 36 | 37 | 38 | class THttpServer(TServer.TServer): 39 | """A simple HTTP-based Thrift server 40 | 41 | This class is not very performant, but it is useful (for example) for 42 | acting as a mock version of an Apache-based PHP Thrift endpoint.""" 43 | 44 | def __init__(self, processor, server_address, 45 | inputProtocolFactory, outputProtocolFactory = None, 46 | server_class = BaseHTTPServer.HTTPServer): 47 | """Set up protocol factories and HTTP server. 48 | 49 | See BaseHTTPServer for server_address. 50 | See TServer for protocol factories.""" 51 | 52 | if outputProtocolFactory is None: 53 | outputProtocolFactory = inputProtocolFactory 54 | 55 | TServer.TServer.__init__(self, processor, None, None, None, 56 | inputProtocolFactory, outputProtocolFactory) 57 | 58 | thttpserver = self 59 | 60 | class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler): 61 | def do_POST(self): 62 | # Don't care about the request path. 63 | itrans = TTransport.TFileObjectTransport(self.rfile) 64 | otrans = TTransport.TFileObjectTransport(self.wfile) 65 | itrans = TTransport.TBufferedTransport(itrans, int(self.headers['Content-Length'])) 66 | otrans = TTransport.TMemoryBuffer() 67 | iprot = thttpserver.inputProtocolFactory.getProtocol(itrans) 68 | oprot = thttpserver.outputProtocolFactory.getProtocol(otrans) 69 | try: 70 | thttpserver.processor.process(iprot, oprot) 71 | except ResponseException, exn: 72 | exn.handler(self) 73 | else: 74 | self.send_response(200) 75 | self.send_header("content-type", "application/x-thrift") 76 | self.end_headers() 77 | self.wfile.write(otrans.getvalue()) 78 | 79 | self.httpd = server_class(server_address, RequestHander) 80 | 81 | def serve(self): 82 | self.httpd.serve_forever() 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pinboard To Evernote 2 | ==================== 3 | About 4 | ----- 5 | Archive the content of your pinboard bookmarks as notes in your evernote account. 6 | 7 | Motivation 8 | ---------- 9 | I really love Pinboard. It is very fast and gets out of the way quickly. But if you don't pay for an annual archival account, then search is rather hit-or-miss. If you don't remember part of the title or the url, then good luck finding the bookmark you are looking for. So, I created this utility which stores the content of your pinboard bookmarks in your evernote account. Consider this a poor man's version of Pinboard archival account. 10 | 11 | Prerequisites 12 | ------------- 13 | * Install [HTML Tidy](http://tidy.sourceforge.net/) `sudo yum install libtidy-devel` OR `sudo apt-get install libtidy-dev` 14 | * Install [PyTidyLib](http://countergram.com/open-source/pytidylib/docs/index.html) `sudo easy_install pytidylib` 15 | * Fill the `credentials.py` file: 16 | * You can find your Pinboard API token on your https://pinboard.in/settings/password page 17 | * You can get a free Diffbot Developer Token from http://www.diffbot.com/plans/free 18 | * You can get a developer token that allows you to access your own Evernote account from: 19 | * https://sandbox.evernote.com/api/DeveloperToken.action (if you want to store notes in a test account at https://sandbox.evernote.com You will need to create a test account first.) 20 | * https://www.evernote.com/api/DeveloperToken.action (if you want to store notes in your main account at https://www.evernote.com) 21 | 22 | Tested on Python 2.7 23 | 24 | Usage 25 | ----- 26 | ``` 27 | main.py [-h] [-s] [-n NOTEBOOK] [-t TAG] 28 | 29 | optional arguments: 30 | -h, --help show this help message and exit 31 | -s, --sandbox Store notes in a test account at sandbox.evernote.com 32 | instead of your main account at www.evernote.com. Use 33 | this option when you are testing the utility. 34 | -n NOTEBOOK, --notebook-name NOTEBOOK 35 | Store the bookmarks in the notebook named NOTEBOOK. If 36 | no notebook is specified then bookmarks are stored in 37 | the default notebook. If NOTEBOOK doesn't exist, then 38 | bookmarks are stored in the default notebook. 39 | -t TAG, --tag TAG Retrieve only those bookmarks from pinboard which are 40 | tagged TAG. 41 | ``` 42 | 43 | Comments 44 | -------- 45 | * If you want to test the utility: 46 | * Create a test account at https://sandbox.evernote.com 47 | * Fill the `credentials.py` file. 48 | * Create a file named `lastUpdate.txt` in the project directory and enter a recent date and time like this `2012-10-20T10:20:30Z`. 49 | * Run `main.py -s` 50 | * This will store the content of your Pinboard bookmarks created after `2012-10-20T10:20:30Z` in your test account at https://sandbox.evernote.com 51 | * If you are satisfied with the results: 52 | * Delete the `lastUpdate.txt` file you created earlier. 53 | * Change your EvernoteDeveloperToken to the one for your main account at https://www.evernote.com 54 | * Create a new notebook in your evernote account (if you don't do this the bookmarks will be stored in your default notebook) 55 | * Run `main.py -n Pinboard` (if the notebook you created was named 'Pinboard') 56 | * Sit back and relax. The first run may take a lot of time depending on the number of bookmarks you have in your Pinboard account. 57 | * Either set up a cron job which executes `main.py -n Pinboard` daily. Or execute the script manually after regular intervals. 58 | -------------------------------------------------------------------------------- /lib/thrift/transport/THttpClient.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from TTransport import * 21 | from cStringIO import StringIO 22 | 23 | import urlparse 24 | import httplib 25 | import warnings 26 | import socket 27 | 28 | class THttpClient(TTransportBase): 29 | 30 | """Http implementation of TTransport base.""" 31 | 32 | def __init__(self, uri_or_host, port=None, path=None): 33 | """THttpClient supports two different types constructor parameters. 34 | 35 | THttpClient(host, port, path) - deprecated 36 | THttpClient(uri) 37 | 38 | Only the second supports https.""" 39 | 40 | if port is not None: 41 | warnings.warn("Please use the THttpClient('http://host:port/path') syntax", DeprecationWarning, stacklevel=2) 42 | self.host = uri_or_host 43 | self.port = port 44 | assert path 45 | self.path = path 46 | self.scheme = 'http' 47 | else: 48 | parsed = urlparse.urlparse(uri_or_host) 49 | self.scheme = parsed.scheme 50 | assert self.scheme in ('http', 'https') 51 | if self.scheme == 'http': 52 | self.port = parsed.port or httplib.HTTP_PORT 53 | elif self.scheme == 'https': 54 | self.port = parsed.port or httplib.HTTPS_PORT 55 | self.host = parsed.hostname 56 | self.path = parsed.path 57 | if parsed.query: 58 | self.path += '?%s' % parsed.query 59 | self.__wbuf = StringIO() 60 | self.__http = None 61 | self.__timeout = None 62 | 63 | def open(self): 64 | if self.scheme == 'http': 65 | self.__http = httplib.HTTP(self.host, self.port) 66 | else: 67 | self.__http = httplib.HTTPS(self.host, self.port) 68 | 69 | def close(self): 70 | self.__http.close() 71 | self.__http = None 72 | 73 | def isOpen(self): 74 | return self.__http != None 75 | 76 | def setTimeout(self, ms): 77 | if not hasattr(socket, 'getdefaulttimeout'): 78 | raise NotImplementedError 79 | 80 | if ms is None: 81 | self.__timeout = None 82 | else: 83 | self.__timeout = ms/1000.0 84 | 85 | def read(self, sz): 86 | return self.__http.file.read(sz) 87 | 88 | def write(self, buf): 89 | self.__wbuf.write(buf) 90 | 91 | def __withTimeout(f): 92 | def _f(*args, **kwargs): 93 | orig_timeout = socket.getdefaulttimeout() 94 | socket.setdefaulttimeout(args[0].__timeout) 95 | result = f(*args, **kwargs) 96 | socket.setdefaulttimeout(orig_timeout) 97 | return result 98 | return _f 99 | 100 | def flush(self): 101 | if self.isOpen(): 102 | self.close() 103 | self.open(); 104 | 105 | # Pull data out of buffer 106 | data = self.__wbuf.getvalue() 107 | self.__wbuf = StringIO() 108 | 109 | # HTTP request 110 | self.__http.putrequest('POST', self.path) 111 | 112 | # Write headers 113 | self.__http.putheader('Host', self.host) 114 | self.__http.putheader('Content-Type', 'application/x-thrift') 115 | self.__http.putheader('Content-Length', str(len(data))) 116 | self.__http.endheaders() 117 | 118 | # Write payload 119 | self.__http.send(data) 120 | 121 | # Get reply to flush the request 122 | self.code, self.message, self.headers = self.__http.getreply() 123 | 124 | # Decorate if we know how to timeout 125 | if hasattr(socket, 'getdefaulttimeout'): 126 | flush = __withTimeout(flush) 127 | -------------------------------------------------------------------------------- /lib/evernote/edam/userstore/UserStore-remote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 4 | # 5 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | # 7 | # options string: py:new_style 8 | # 9 | 10 | import sys 11 | import pprint 12 | from urlparse import urlparse 13 | from thrift.transport import TTransport 14 | from thrift.transport import TSocket 15 | from thrift.transport import THttpClient 16 | from thrift.protocol import TBinaryProtocol 17 | 18 | import UserStore 19 | from ttypes import * 20 | 21 | if len(sys.argv) <= 1 or sys.argv[1] == '--help': 22 | print '' 23 | print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]' 24 | print '' 25 | print 'Functions:' 26 | print ' bool checkVersion(string clientName, i16 edamVersionMajor, i16 edamVersionMinor)' 27 | print ' BootstrapInfo getBootstrapInfo(string locale)' 28 | print ' AuthenticationResult authenticate(string username, string password, string consumerKey, string consumerSecret)' 29 | print ' AuthenticationResult refreshAuthentication(string authenticationToken)' 30 | print ' User getUser(string authenticationToken)' 31 | print ' PublicUserInfo getPublicUserInfo(string username)' 32 | print ' PremiumInfo getPremiumInfo(string authenticationToken)' 33 | print ' string getNoteStoreUrl(string authenticationToken)' 34 | print '' 35 | sys.exit(0) 36 | 37 | pp = pprint.PrettyPrinter(indent = 2) 38 | host = 'localhost' 39 | port = 9090 40 | uri = '' 41 | framed = False 42 | http = False 43 | argi = 1 44 | 45 | if sys.argv[argi] == '-h': 46 | parts = sys.argv[argi+1].split(':') 47 | host = parts[0] 48 | if len(parts) > 1: 49 | port = int(parts[1]) 50 | argi += 2 51 | 52 | if sys.argv[argi] == '-u': 53 | url = urlparse(sys.argv[argi+1]) 54 | parts = url[1].split(':') 55 | host = parts[0] 56 | if len(parts) > 1: 57 | port = int(parts[1]) 58 | else: 59 | port = 80 60 | uri = url[2] 61 | if url[4]: 62 | uri += '?%s' % url[4] 63 | http = True 64 | argi += 2 65 | 66 | if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed': 67 | framed = True 68 | argi += 1 69 | 70 | cmd = sys.argv[argi] 71 | args = sys.argv[argi+1:] 72 | 73 | if http: 74 | transport = THttpClient.THttpClient(host, port, uri) 75 | else: 76 | socket = TSocket.TSocket(host, port) 77 | if framed: 78 | transport = TTransport.TFramedTransport(socket) 79 | else: 80 | transport = TTransport.TBufferedTransport(socket) 81 | protocol = TBinaryProtocol.TBinaryProtocol(transport) 82 | client = UserStore.Client(protocol) 83 | transport.open() 84 | 85 | if cmd == 'checkVersion': 86 | if len(args) != 3: 87 | print 'checkVersion requires 3 args' 88 | sys.exit(1) 89 | pp.pprint(client.checkVersion(args[0],eval(args[1]),eval(args[2]),)) 90 | 91 | elif cmd == 'getBootstrapInfo': 92 | if len(args) != 1: 93 | print 'getBootstrapInfo requires 1 args' 94 | sys.exit(1) 95 | pp.pprint(client.getBootstrapInfo(args[0],)) 96 | 97 | elif cmd == 'authenticate': 98 | if len(args) != 4: 99 | print 'authenticate requires 4 args' 100 | sys.exit(1) 101 | pp.pprint(client.authenticate(args[0],args[1],args[2],args[3],)) 102 | 103 | elif cmd == 'refreshAuthentication': 104 | if len(args) != 1: 105 | print 'refreshAuthentication requires 1 args' 106 | sys.exit(1) 107 | pp.pprint(client.refreshAuthentication(args[0],)) 108 | 109 | elif cmd == 'getUser': 110 | if len(args) != 1: 111 | print 'getUser requires 1 args' 112 | sys.exit(1) 113 | pp.pprint(client.getUser(args[0],)) 114 | 115 | elif cmd == 'getPublicUserInfo': 116 | if len(args) != 1: 117 | print 'getPublicUserInfo requires 1 args' 118 | sys.exit(1) 119 | pp.pprint(client.getPublicUserInfo(args[0],)) 120 | 121 | elif cmd == 'getPremiumInfo': 122 | if len(args) != 1: 123 | print 'getPremiumInfo requires 1 args' 124 | sys.exit(1) 125 | pp.pprint(client.getPremiumInfo(args[0],)) 126 | 127 | elif cmd == 'getNoteStoreUrl': 128 | if len(args) != 1: 129 | print 'getNoteStoreUrl requires 1 args' 130 | sys.exit(1) 131 | pp.pprint(client.getNoteStoreUrl(args[0],)) 132 | 133 | else: 134 | print 'Unrecognized method %s' % cmd 135 | sys.exit(1) 136 | 137 | transport.close() 138 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import traceback 4 | import datetime 5 | import argparse 6 | from credentials import * 7 | from pinboardHelper import * 8 | from diffbotHelper import * 9 | from sanitize import * 10 | from evernoteHelper import * 11 | 12 | 13 | def main(): 14 | 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument("-s", "--sandbox", action="store_true", help="Store notes in a test account at sandbox.evernote.com instead of your main account at www.evernote.com. Use this option when you are testing the utility.") 17 | parser.add_argument("-n", "--notebook-name", metavar="NOTEBOOK", help="Store the bookmarks in the notebook named NOTEBOOK. If no notebook is specified then bookmarks are stored in the default notebook. If NOTEBOOK doesn't exist, then bookmarks are stored in the default notebook.") 18 | parser.add_argument("-t", "--tag", metavar="TAG", help="Retrieve only those bookmarks from pinboard which are tagged TAG.") 19 | args = parser.parse_args() 20 | 21 | print "Fetching bookmarks from Pinboard..." 22 | try: 23 | f = open("lastUpdate.txt", "r") 24 | fromdt = f.read().strip() 25 | f.close() 26 | firstUse = False 27 | print "Last fetched on: %s" % fromdt 28 | except IOError: 29 | # If lastUpdate.txt doesn't exist. 30 | # It means that the program is being run for the first time. 31 | # So get all bookmarks. 32 | print "Last fetched on: Never" 33 | firstUse = True 34 | 35 | try: 36 | if firstUse: 37 | if args.tag: 38 | bookmarkList = getAllBookmarksTagged(PinboardAPIToken, args.tag) 39 | else: 40 | bookmarkList = getAllBookmarks(PinboardAPIToken) 41 | else: 42 | if args.tag: 43 | bookmarkList = getBookmarksFromDateTagged(PinboardAPIToken, fromdt, args.tag) 44 | else: 45 | bookmarkList = getBookmarksFromDate(PinboardAPIToken, fromdt) 46 | except: 47 | print "Fetching bookmarks from Pinboard failed." 48 | traceback.print_exc() 49 | print 50 | exit(1) 51 | 52 | # We have fetched bookmarks uptill now. 53 | todt = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") 54 | 55 | totalBookmarks = len(bookmarkList) 56 | notesCreated = 0 57 | diffbotErrors = 0 58 | sanitizeErrors = 0 59 | evernoteErrors = 0 60 | 61 | print "Total bookmarks = " + str(totalBookmarks) 62 | 63 | try: 64 | print "Initializing EvernoteHelper..." 65 | if args.sandbox: 66 | evernoteHelper = EvernoteHelper(EvernoteDeveloperToken, production=False) 67 | else: 68 | evernoteHelper = EvernoteHelper(EvernoteDeveloperToken, production=True) 69 | except: 70 | print "Creating EvernoteHelper failed." 71 | traceback.print_exc() 72 | print 73 | exit(1) 74 | 75 | failedURLs = open("failedURLs.txt", "a") 76 | 77 | print 78 | for bookmark in reversed(bookmarkList): 79 | 80 | try: 81 | print "Extracting article using Diffbot..." 82 | html = extractArticle(DiffbotToken, bookmark[0], html=True) 83 | except: 84 | print "Extracting article using Diffbot failed." 85 | print bookmark[0] 86 | failedURLs.write(bookmark[0] + "\n") 87 | traceback.print_exc() 88 | print 89 | diffbotErrors += 1 90 | continue 91 | 92 | try: 93 | print "Converting article from HTML to ENML..." 94 | enml = sanitize(html) 95 | except: 96 | print "Converting article from HTML to ENML failed." 97 | print bookmark[0] 98 | failedURLs.write(bookmark[0] + "\n") 99 | traceback.print_exc() 100 | print 101 | sanitizeErrors += 1 102 | continue 103 | 104 | try: 105 | print "Storing note in Evernote..." 106 | evernoteHelper.sendToEvernote(bookmark[1], bookmark[0], enml, notebookName=args.notebook_name) 107 | except: 108 | print "Storing note in Evernote failed." 109 | print bookmark[0] 110 | failedURLs.write(bookmark[0] + "\n") 111 | traceback.print_exc() 112 | print 113 | evernoteErrors += 1 114 | continue 115 | 116 | notesCreated += 1 117 | 118 | failedURLs.close() 119 | 120 | # Update the lastUpdate time. 121 | f = open("lastUpdate.txt", "w") 122 | f.write(todt) 123 | f.close() 124 | 125 | print 126 | print "Total bookmarks = " + str(totalBookmarks) 127 | print "Total number of notes created = " + str(notesCreated) 128 | print "Diffbot Errors = " + str(diffbotErrors) 129 | print "Sanitize Errors = " + str(sanitizeErrors) 130 | print "Evernote Errors = " + str(evernoteErrors) 131 | print 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /lib/thrift/server/TProcessPoolServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | 21 | import logging 22 | from multiprocessing import Process, Value, Condition, reduction 23 | 24 | from TServer import TServer 25 | from thrift.transport.TTransport import TTransportException 26 | 27 | class TProcessPoolServer(TServer): 28 | 29 | """ 30 | Server with a fixed size pool of worker subprocesses which service requests. 31 | Note that if you need shared state between the handlers - it's up to you! 32 | Written by Dvir Volk, doat.com 33 | """ 34 | 35 | def __init__(self, * args): 36 | TServer.__init__(self, *args) 37 | self.numWorkers = 10 38 | self.workers = [] 39 | self.isRunning = Value('b', False) 40 | self.stopCondition = Condition() 41 | self.postForkCallback = None 42 | 43 | def setPostForkCallback(self, callback): 44 | if not callable(callback): 45 | raise TypeError("This is not a callback!") 46 | self.postForkCallback = callback 47 | 48 | def setNumWorkers(self, num): 49 | """Set the number of worker threads that should be created""" 50 | self.numWorkers = num 51 | 52 | def workerProcess(self): 53 | """Loop around getting clients from the shared queue and process them.""" 54 | 55 | if self.postForkCallback: 56 | self.postForkCallback() 57 | 58 | while self.isRunning.value == True: 59 | try: 60 | client = self.serverTransport.accept() 61 | self.serveClient(client) 62 | except (KeyboardInterrupt, SystemExit): 63 | return 0 64 | except Exception, x: 65 | logging.exception(x) 66 | 67 | def serveClient(self, client): 68 | """Process input/output from a client for as long as possible""" 69 | itrans = self.inputTransportFactory.getTransport(client) 70 | otrans = self.outputTransportFactory.getTransport(client) 71 | iprot = self.inputProtocolFactory.getProtocol(itrans) 72 | oprot = self.outputProtocolFactory.getProtocol(otrans) 73 | 74 | try: 75 | while True: 76 | self.processor.process(iprot, oprot) 77 | except TTransportException, tx: 78 | pass 79 | except Exception, x: 80 | logging.exception(x) 81 | 82 | itrans.close() 83 | otrans.close() 84 | 85 | 86 | def serve(self): 87 | """Start a fixed number of worker threads and put client into a queue""" 88 | 89 | #this is a shared state that can tell the workers to exit when set as false 90 | self.isRunning.value = True 91 | 92 | #first bind and listen to the port 93 | self.serverTransport.listen() 94 | 95 | #fork the children 96 | for i in range(self.numWorkers): 97 | try: 98 | w = Process(target=self.workerProcess) 99 | w.daemon = True 100 | w.start() 101 | self.workers.append(w) 102 | except Exception, x: 103 | logging.exception(x) 104 | 105 | #wait until the condition is set by stop() 106 | 107 | while True: 108 | 109 | self.stopCondition.acquire() 110 | try: 111 | self.stopCondition.wait() 112 | break 113 | except (SystemExit, KeyboardInterrupt): 114 | break 115 | except Exception, x: 116 | logging.exception(x) 117 | 118 | self.isRunning.value = False 119 | 120 | def stop(self): 121 | self.isRunning.value = False 122 | self.stopCondition.acquire() 123 | self.stopCondition.notify() 124 | self.stopCondition.release() 125 | 126 | -------------------------------------------------------------------------------- /lib/thrift/Thrift.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import sys 21 | 22 | class TType: 23 | STOP = 0 24 | VOID = 1 25 | BOOL = 2 26 | BYTE = 3 27 | I08 = 3 28 | DOUBLE = 4 29 | I16 = 6 30 | I32 = 8 31 | I64 = 10 32 | STRING = 11 33 | UTF7 = 11 34 | STRUCT = 12 35 | MAP = 13 36 | SET = 14 37 | LIST = 15 38 | UTF8 = 16 39 | UTF16 = 17 40 | 41 | _VALUES_TO_NAMES = ( 'STOP', 42 | 'VOID', 43 | 'BOOL', 44 | 'BYTE', 45 | 'DOUBLE', 46 | None, 47 | 'I16', 48 | None, 49 | 'I32', 50 | None, 51 | 'I64', 52 | 'STRING', 53 | 'STRUCT', 54 | 'MAP', 55 | 'SET', 56 | 'LIST', 57 | 'UTF8', 58 | 'UTF16' ) 59 | 60 | class TMessageType: 61 | CALL = 1 62 | REPLY = 2 63 | EXCEPTION = 3 64 | ONEWAY = 4 65 | 66 | class TProcessor: 67 | 68 | """Base class for procsessor, which works on two streams.""" 69 | 70 | def process(iprot, oprot): 71 | pass 72 | 73 | class TException(Exception): 74 | 75 | """Base class for all thrift exceptions.""" 76 | 77 | # BaseException.message is deprecated in Python v[2.6,3.0) 78 | if (2,6,0) <= sys.version_info < (3,0): 79 | def _get_message(self): 80 | return self._message 81 | def _set_message(self, message): 82 | self._message = message 83 | message = property(_get_message, _set_message) 84 | 85 | def __init__(self, message=None): 86 | Exception.__init__(self, message) 87 | self.message = message 88 | 89 | class TApplicationException(TException): 90 | 91 | """Application level thrift exceptions.""" 92 | 93 | UNKNOWN = 0 94 | UNKNOWN_METHOD = 1 95 | INVALID_MESSAGE_TYPE = 2 96 | WRONG_METHOD_NAME = 3 97 | BAD_SEQUENCE_ID = 4 98 | MISSING_RESULT = 5 99 | INTERNAL_ERROR = 6 100 | PROTOCOL_ERROR = 7 101 | 102 | def __init__(self, type=UNKNOWN, message=None): 103 | TException.__init__(self, message) 104 | self.type = type 105 | 106 | def __str__(self): 107 | if self.message: 108 | return self.message 109 | elif self.type == self.UNKNOWN_METHOD: 110 | return 'Unknown method' 111 | elif self.type == self.INVALID_MESSAGE_TYPE: 112 | return 'Invalid message type' 113 | elif self.type == self.WRONG_METHOD_NAME: 114 | return 'Wrong method name' 115 | elif self.type == self.BAD_SEQUENCE_ID: 116 | return 'Bad sequence ID' 117 | elif self.type == self.MISSING_RESULT: 118 | return 'Missing result' 119 | else: 120 | return 'Default (unknown) TApplicationException' 121 | 122 | def read(self, iprot): 123 | iprot.readStructBegin() 124 | while True: 125 | (fname, ftype, fid) = iprot.readFieldBegin() 126 | if ftype == TType.STOP: 127 | break 128 | if fid == 1: 129 | if ftype == TType.STRING: 130 | self.message = iprot.readString(); 131 | else: 132 | iprot.skip(ftype) 133 | elif fid == 2: 134 | if ftype == TType.I32: 135 | self.type = iprot.readI32(); 136 | else: 137 | iprot.skip(ftype) 138 | else: 139 | iprot.skip(ftype) 140 | iprot.readFieldEnd() 141 | iprot.readStructEnd() 142 | 143 | def write(self, oprot): 144 | oprot.writeStructBegin('TApplicationException') 145 | if self.message != None: 146 | oprot.writeFieldBegin('message', TType.STRING, 1) 147 | oprot.writeString(self.message) 148 | oprot.writeFieldEnd() 149 | if self.type != None: 150 | oprot.writeFieldBegin('type', TType.I32, 2) 151 | oprot.writeI32(self.type) 152 | oprot.writeFieldEnd() 153 | oprot.writeFieldStop() 154 | oprot.writeStructEnd() 155 | -------------------------------------------------------------------------------- /lib/thrift/transport/TSocket.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from TTransport import * 21 | import os 22 | import errno 23 | import socket 24 | import sys 25 | 26 | class TSocketBase(TTransportBase): 27 | def _resolveAddr(self): 28 | if self._unix_socket is not None: 29 | return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None, self._unix_socket)] 30 | else: 31 | return socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG) 32 | 33 | def close(self): 34 | if self.handle: 35 | self.handle.close() 36 | self.handle = None 37 | 38 | class TSocket(TSocketBase): 39 | """Socket implementation of TTransport base.""" 40 | 41 | def __init__(self, host='localhost', port=9090, unix_socket=None): 42 | """Initialize a TSocket 43 | 44 | @param host(str) The host to connect to. 45 | @param port(int) The (TCP) port to connect to. 46 | @param unix_socket(str) The filename of a unix socket to connect to. 47 | (host and port will be ignored.) 48 | """ 49 | 50 | self.host = host 51 | self.port = port 52 | self.handle = None 53 | self._unix_socket = unix_socket 54 | self._timeout = None 55 | 56 | def setHandle(self, h): 57 | self.handle = h 58 | 59 | def isOpen(self): 60 | return self.handle is not None 61 | 62 | def setTimeout(self, ms): 63 | if ms is None: 64 | self._timeout = None 65 | else: 66 | self._timeout = ms/1000.0 67 | 68 | if self.handle is not None: 69 | self.handle.settimeout(self._timeout) 70 | 71 | def open(self): 72 | try: 73 | res0 = self._resolveAddr() 74 | for res in res0: 75 | self.handle = socket.socket(res[0], res[1]) 76 | self.handle.settimeout(self._timeout) 77 | try: 78 | self.handle.connect(res[4]) 79 | except socket.error, e: 80 | if res is not res0[-1]: 81 | continue 82 | else: 83 | raise e 84 | break 85 | except socket.error, e: 86 | if self._unix_socket: 87 | message = 'Could not connect to socket %s' % self._unix_socket 88 | else: 89 | message = 'Could not connect to %s:%d' % (self.host, self.port) 90 | raise TTransportException(type=TTransportException.NOT_OPEN, message=message) 91 | 92 | def read(self, sz): 93 | try: 94 | buff = self.handle.recv(sz) 95 | except socket.error, e: 96 | if (e.args[0] == errno.ECONNRESET and 97 | (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))): 98 | # freebsd and Mach don't follow POSIX semantic of recv 99 | # and fail with ECONNRESET if peer performed shutdown. 100 | # See corresponding comment and code in TSocket::read() 101 | # in lib/cpp/src/transport/TSocket.cpp. 102 | self.close() 103 | # Trigger the check to raise the END_OF_FILE exception below. 104 | buff = '' 105 | else: 106 | raise 107 | if len(buff) == 0: 108 | raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes') 109 | return buff 110 | 111 | def write(self, buff): 112 | if not self.handle: 113 | raise TTransportException(type=TTransportException.NOT_OPEN, message='Transport not open') 114 | sent = 0 115 | have = len(buff) 116 | while sent < have: 117 | plus = self.handle.send(buff) 118 | if plus == 0: 119 | raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket sent 0 bytes') 120 | sent += plus 121 | buff = buff[plus:] 122 | 123 | def flush(self): 124 | pass 125 | 126 | class TServerSocket(TSocketBase, TServerTransportBase): 127 | """Socket implementation of TServerTransport base.""" 128 | 129 | def __init__(self, host=None, port=9090, unix_socket=None): 130 | self.host = host 131 | self.port = port 132 | self._unix_socket = unix_socket 133 | self.handle = None 134 | 135 | def listen(self): 136 | res0 = self._resolveAddr() 137 | for res in res0: 138 | if res[0] is socket.AF_INET6 or res is res0[-1]: 139 | break 140 | 141 | # We need remove the old unix socket if the file exists and 142 | # nobody is listening on it. 143 | if self._unix_socket: 144 | tmp = socket.socket(res[0], res[1]) 145 | try: 146 | tmp.connect(res[4]) 147 | except socket.error, err: 148 | eno, message = err.args 149 | if eno == errno.ECONNREFUSED: 150 | os.unlink(res[4]) 151 | 152 | self.handle = socket.socket(res[0], res[1]) 153 | self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 154 | if hasattr(self.handle, 'settimeout'): 155 | self.handle.settimeout(None) 156 | self.handle.bind(res[4]) 157 | self.handle.listen(128) 158 | 159 | def accept(self): 160 | client, addr = self.handle.accept() 161 | result = TSocket() 162 | result.setHandle(client) 163 | return result 164 | -------------------------------------------------------------------------------- /lib/evernote/edam/limits/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_ATTRIBUTE_LEN_MIN = 1 13 | EDAM_ATTRIBUTE_LEN_MAX = 4096 14 | EDAM_ATTRIBUTE_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{1,4096}$" 15 | EDAM_ATTRIBUTE_LIST_MAX = 100 16 | EDAM_ATTRIBUTE_MAP_MAX = 100 17 | EDAM_GUID_LEN_MIN = 36 18 | EDAM_GUID_LEN_MAX = 36 19 | EDAM_GUID_REGEX = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" 20 | EDAM_EMAIL_LEN_MIN = 6 21 | EDAM_EMAIL_LEN_MAX = 255 22 | EDAM_EMAIL_LOCAL_REGEX = "^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*$" 23 | EDAM_EMAIL_DOMAIN_REGEX = "^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.([A-Za-z]{2,})$" 24 | EDAM_EMAIL_REGEX = "^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.([A-Za-z]{2,})$" 25 | EDAM_TIMEZONE_LEN_MIN = 1 26 | EDAM_TIMEZONE_LEN_MAX = 32 27 | EDAM_TIMEZONE_REGEX = "^([A-Za-z_-]+(/[A-Za-z_-]+)*)|(GMT(-|\\+)[0-9]{1,2}(:[0-9]{2})?)$" 28 | EDAM_MIME_LEN_MIN = 3 29 | EDAM_MIME_LEN_MAX = 255 30 | EDAM_MIME_REGEX = "^[A-Za-z]+/[A-Za-z0-9._+-]+$" 31 | EDAM_MIME_TYPE_GIF = "image/gif" 32 | EDAM_MIME_TYPE_JPEG = "image/jpeg" 33 | EDAM_MIME_TYPE_PNG = "image/png" 34 | EDAM_MIME_TYPE_WAV = "audio/wav" 35 | EDAM_MIME_TYPE_MP3 = "audio/mpeg" 36 | EDAM_MIME_TYPE_AMR = "audio/amr" 37 | EDAM_MIME_TYPE_MP4_VIDEO = "video/mp4" 38 | EDAM_MIME_TYPE_INK = "application/vnd.evernote.ink" 39 | EDAM_MIME_TYPE_PDF = "application/pdf" 40 | EDAM_MIME_TYPE_DEFAULT = "application/octet-stream" 41 | EDAM_MIME_TYPES = set([ 42 | "image/gif", 43 | "image/jpeg", 44 | "image/png", 45 | "audio/wav", 46 | "audio/mpeg", 47 | "audio/amr", 48 | "application/vnd.evernote.ink", 49 | "application/pdf", 50 | "video/mp4", 51 | ]) 52 | EDAM_COMMERCE_SERVICE_GOOGLE = "Google" 53 | EDAM_COMMERCE_SERVICE_PAYPAL = "Paypal" 54 | EDAM_COMMERCE_SERVICE_GIFT = "Gift" 55 | EDAM_COMMERCE_SERVICE_TRIALPAY = "TrialPay" 56 | EDAM_COMMERCE_SERVICE_TRIAL = "Trial" 57 | EDAM_COMMERCE_SERVICE_GROUP = "Group" 58 | EDAM_COMMERCE_SERVICE_CYBERSOURCE = "CYBERSRC" 59 | EDAM_COMMERCE_DEFAULT_CURRENCY_COUNTRY_CODE = "USD" 60 | EDAM_SEARCH_QUERY_LEN_MIN = 0 61 | EDAM_SEARCH_QUERY_LEN_MAX = 1024 62 | EDAM_SEARCH_QUERY_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{0,1024}$" 63 | EDAM_HASH_LEN = 16 64 | EDAM_USER_USERNAME_LEN_MIN = 1 65 | EDAM_USER_USERNAME_LEN_MAX = 64 66 | EDAM_USER_USERNAME_REGEX = "^[a-z0-9]([a-z0-9_-]{0,62}[a-z0-9])?$" 67 | EDAM_USER_NAME_LEN_MIN = 1 68 | EDAM_USER_NAME_LEN_MAX = 255 69 | EDAM_USER_NAME_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{1,255}$" 70 | EDAM_TAG_NAME_LEN_MIN = 1 71 | EDAM_TAG_NAME_LEN_MAX = 100 72 | EDAM_TAG_NAME_REGEX = "^[^,\\p{Cc}\\p{Z}]([^,\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^,\\p{Cc}\\p{Z}])?$" 73 | EDAM_NOTE_TITLE_LEN_MIN = 1 74 | EDAM_NOTE_TITLE_LEN_MAX = 255 75 | EDAM_NOTE_TITLE_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,253}[^\\p{Cc}\\p{Z}])?$" 76 | EDAM_NOTE_CONTENT_LEN_MIN = 0 77 | EDAM_NOTE_CONTENT_LEN_MAX = 5242880 78 | EDAM_APPLICATIONDATA_NAME_LEN_MIN = 3 79 | EDAM_APPLICATIONDATA_NAME_LEN_MAX = 32 80 | EDAM_APPLICATIONDATA_VALUE_LEN_MIN = 0 81 | EDAM_APPLICATIONDATA_VALUE_LEN_MAX = 4092 82 | EDAM_APPLICATIONDATA_ENTRY_LEN_MAX = 4095 83 | EDAM_APPLICATIONDATA_NAME_REGEX = "^[A-Za-z0-9_.-]{3,32}$" 84 | EDAM_APPLICATIONDATA_VALUE_REGEX = "^[^\\p{Cc}]{0,4092}$" 85 | EDAM_NOTEBOOK_NAME_LEN_MIN = 1 86 | EDAM_NOTEBOOK_NAME_LEN_MAX = 100 87 | EDAM_NOTEBOOK_NAME_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 88 | EDAM_NOTEBOOK_STACK_LEN_MIN = 1 89 | EDAM_NOTEBOOK_STACK_LEN_MAX = 100 90 | EDAM_NOTEBOOK_STACK_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 91 | EDAM_PUBLISHING_URI_LEN_MIN = 1 92 | EDAM_PUBLISHING_URI_LEN_MAX = 255 93 | EDAM_PUBLISHING_URI_REGEX = "^[a-zA-Z0-9.~_+-]{1,255}$" 94 | EDAM_PUBLISHING_URI_PROHIBITED = set([ 95 | "..", 96 | ]) 97 | EDAM_PUBLISHING_DESCRIPTION_LEN_MIN = 1 98 | EDAM_PUBLISHING_DESCRIPTION_LEN_MAX = 200 99 | EDAM_PUBLISHING_DESCRIPTION_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,198}[^\\p{Cc}\\p{Z}])?$" 100 | EDAM_SAVED_SEARCH_NAME_LEN_MIN = 1 101 | EDAM_SAVED_SEARCH_NAME_LEN_MAX = 100 102 | EDAM_SAVED_SEARCH_NAME_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 103 | EDAM_USER_PASSWORD_LEN_MIN = 6 104 | EDAM_USER_PASSWORD_LEN_MAX = 64 105 | EDAM_USER_PASSWORD_REGEX = "^[A-Za-z0-9!#$%&'()*+,./:;<=>?@^_`{|}~\\[\\]\\\\-]{6,64}$" 106 | EDAM_NOTE_TAGS_MAX = 100 107 | EDAM_NOTE_RESOURCES_MAX = 1000 108 | EDAM_USER_TAGS_MAX = 100000 109 | EDAM_USER_SAVED_SEARCHES_MAX = 100 110 | EDAM_USER_NOTES_MAX = 100000 111 | EDAM_USER_NOTEBOOKS_MAX = 250 112 | EDAM_USER_RECENT_MAILED_ADDRESSES_MAX = 10 113 | EDAM_USER_MAIL_LIMIT_DAILY_FREE = 50 114 | EDAM_USER_MAIL_LIMIT_DAILY_PREMIUM = 200 115 | EDAM_USER_UPLOAD_LIMIT_FREE = 62914560 116 | EDAM_USER_UPLOAD_LIMIT_PREMIUM = 1073741824 117 | EDAM_NOTE_SIZE_MAX_FREE = 26214400 118 | EDAM_NOTE_SIZE_MAX_PREMIUM = 52428800 119 | EDAM_RESOURCE_SIZE_MAX_FREE = 26214400 120 | EDAM_RESOURCE_SIZE_MAX_PREMIUM = 52428800 121 | EDAM_USER_LINKED_NOTEBOOK_MAX = 100 122 | EDAM_NOTEBOOK_SHARED_NOTEBOOK_MAX = 250 123 | EDAM_NOTE_CONTENT_CLASS_LEN_MIN = 3 124 | EDAM_NOTE_CONTENT_CLASS_LEN_MAX = 32 125 | EDAM_HELLO_APP_CONTENT_CLASS_PREFIX = "evernote.hello." 126 | EDAM_FOOD_APP_CONTENT_CLASS_PREFIX = "evernote.food." 127 | EDAM_NOTE_CONTENT_CLASS_REGEX = "^[A-Za-z0-9_.-]{3,32}$" 128 | EDAM_CONTENT_CLASS_HELLO_ENCOUNTER = "evernote.hello.encounter" 129 | EDAM_CONTENT_CLASS_HELLO_PROFILE = "evernote.hello.profile" 130 | EDAM_CONTENT_CLASS_FOOD_MEAL = "evernote.food.meal" 131 | EDAM_CONTENT_CLASS_SKITCH = "evernote.skitch" 132 | EDAM_RELATED_PLAINTEXT_LEN_MIN = 1 133 | EDAM_RELATED_PLAINTEXT_LEN_MAX = 131072 134 | EDAM_RELATED_MAX_NOTES = 25 135 | EDAM_RELATED_MAX_NOTEBOOKS = 1 136 | EDAM_RELATED_MAX_TAGS = 25 137 | -------------------------------------------------------------------------------- /lib/thrift/transport/TTwisted.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | from zope.interface import implements, Interface, Attribute 20 | from twisted.internet.protocol import Protocol, ServerFactory, ClientFactory, \ 21 | connectionDone 22 | from twisted.internet import defer 23 | from twisted.protocols import basic 24 | from twisted.python import log 25 | from twisted.web import server, resource, http 26 | 27 | from thrift.transport import TTransport 28 | from cStringIO import StringIO 29 | 30 | 31 | class TMessageSenderTransport(TTransport.TTransportBase): 32 | 33 | def __init__(self): 34 | self.__wbuf = StringIO() 35 | 36 | def write(self, buf): 37 | self.__wbuf.write(buf) 38 | 39 | def flush(self): 40 | msg = self.__wbuf.getvalue() 41 | self.__wbuf = StringIO() 42 | self.sendMessage(msg) 43 | 44 | def sendMessage(self, message): 45 | raise NotImplementedError 46 | 47 | 48 | class TCallbackTransport(TMessageSenderTransport): 49 | 50 | def __init__(self, func): 51 | TMessageSenderTransport.__init__(self) 52 | self.func = func 53 | 54 | def sendMessage(self, message): 55 | self.func(message) 56 | 57 | 58 | class ThriftClientProtocol(basic.Int32StringReceiver): 59 | 60 | MAX_LENGTH = 2 ** 31 - 1 61 | 62 | def __init__(self, client_class, iprot_factory, oprot_factory=None): 63 | self._client_class = client_class 64 | self._iprot_factory = iprot_factory 65 | if oprot_factory is None: 66 | self._oprot_factory = iprot_factory 67 | else: 68 | self._oprot_factory = oprot_factory 69 | 70 | self.recv_map = {} 71 | self.started = defer.Deferred() 72 | 73 | def dispatch(self, msg): 74 | self.sendString(msg) 75 | 76 | def connectionMade(self): 77 | tmo = TCallbackTransport(self.dispatch) 78 | self.client = self._client_class(tmo, self._oprot_factory) 79 | self.started.callback(self.client) 80 | 81 | def connectionLost(self, reason=connectionDone): 82 | for k,v in self.client._reqs.iteritems(): 83 | tex = TTransport.TTransportException( 84 | type=TTransport.TTransportException.END_OF_FILE, 85 | message='Connection closed') 86 | v.errback(tex) 87 | 88 | def stringReceived(self, frame): 89 | tr = TTransport.TMemoryBuffer(frame) 90 | iprot = self._iprot_factory.getProtocol(tr) 91 | (fname, mtype, rseqid) = iprot.readMessageBegin() 92 | 93 | try: 94 | method = self.recv_map[fname] 95 | except KeyError: 96 | method = getattr(self.client, 'recv_' + fname) 97 | self.recv_map[fname] = method 98 | 99 | method(iprot, mtype, rseqid) 100 | 101 | 102 | class ThriftServerProtocol(basic.Int32StringReceiver): 103 | 104 | MAX_LENGTH = 2 ** 31 - 1 105 | 106 | def dispatch(self, msg): 107 | self.sendString(msg) 108 | 109 | def processError(self, error): 110 | self.transport.loseConnection() 111 | 112 | def processOk(self, _, tmo): 113 | msg = tmo.getvalue() 114 | 115 | if len(msg) > 0: 116 | self.dispatch(msg) 117 | 118 | def stringReceived(self, frame): 119 | tmi = TTransport.TMemoryBuffer(frame) 120 | tmo = TTransport.TMemoryBuffer() 121 | 122 | iprot = self.factory.iprot_factory.getProtocol(tmi) 123 | oprot = self.factory.oprot_factory.getProtocol(tmo) 124 | 125 | d = self.factory.processor.process(iprot, oprot) 126 | d.addCallbacks(self.processOk, self.processError, 127 | callbackArgs=(tmo,)) 128 | 129 | 130 | class IThriftServerFactory(Interface): 131 | 132 | processor = Attribute("Thrift processor") 133 | 134 | iprot_factory = Attribute("Input protocol factory") 135 | 136 | oprot_factory = Attribute("Output protocol factory") 137 | 138 | 139 | class IThriftClientFactory(Interface): 140 | 141 | client_class = Attribute("Thrift client class") 142 | 143 | iprot_factory = Attribute("Input protocol factory") 144 | 145 | oprot_factory = Attribute("Output protocol factory") 146 | 147 | 148 | class ThriftServerFactory(ServerFactory): 149 | 150 | implements(IThriftServerFactory) 151 | 152 | protocol = ThriftServerProtocol 153 | 154 | def __init__(self, processor, iprot_factory, oprot_factory=None): 155 | self.processor = processor 156 | self.iprot_factory = iprot_factory 157 | if oprot_factory is None: 158 | self.oprot_factory = iprot_factory 159 | else: 160 | self.oprot_factory = oprot_factory 161 | 162 | 163 | class ThriftClientFactory(ClientFactory): 164 | 165 | implements(IThriftClientFactory) 166 | 167 | protocol = ThriftClientProtocol 168 | 169 | def __init__(self, client_class, iprot_factory, oprot_factory=None): 170 | self.client_class = client_class 171 | self.iprot_factory = iprot_factory 172 | if oprot_factory is None: 173 | self.oprot_factory = iprot_factory 174 | else: 175 | self.oprot_factory = oprot_factory 176 | 177 | def buildProtocol(self, addr): 178 | p = self.protocol(self.client_class, self.iprot_factory, 179 | self.oprot_factory) 180 | p.factory = self 181 | return p 182 | 183 | 184 | class ThriftResource(resource.Resource): 185 | 186 | allowedMethods = ('POST',) 187 | 188 | def __init__(self, processor, inputProtocolFactory, 189 | outputProtocolFactory=None): 190 | resource.Resource.__init__(self) 191 | self.inputProtocolFactory = inputProtocolFactory 192 | if outputProtocolFactory is None: 193 | self.outputProtocolFactory = inputProtocolFactory 194 | else: 195 | self.outputProtocolFactory = outputProtocolFactory 196 | self.processor = processor 197 | 198 | def getChild(self, path, request): 199 | return self 200 | 201 | def _cbProcess(self, _, request, tmo): 202 | msg = tmo.getvalue() 203 | request.setResponseCode(http.OK) 204 | request.setHeader("content-type", "application/x-thrift") 205 | request.write(msg) 206 | request.finish() 207 | 208 | def render_POST(self, request): 209 | request.content.seek(0, 0) 210 | data = request.content.read() 211 | tmi = TTransport.TMemoryBuffer(data) 212 | tmo = TTransport.TMemoryBuffer() 213 | 214 | iprot = self.inputProtocolFactory.getProtocol(tmi) 215 | oprot = self.outputProtocolFactory.getProtocol(tmo) 216 | 217 | d = self.processor.process(iprot, oprot) 218 | d.addCallback(self._cbProcess, request, tmo) 219 | return server.NOT_DONE_YET 220 | -------------------------------------------------------------------------------- /lib/thrift/transport/TSSLSocket.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | import os 20 | import socket 21 | import ssl 22 | 23 | from thrift.transport import TSocket 24 | from thrift.transport.TTransport import TTransportException 25 | 26 | class TSSLSocket(TSocket.TSocket): 27 | """ 28 | SSL implementation of client-side TSocket 29 | 30 | This class creates outbound sockets wrapped using the 31 | python standard ssl module for encrypted connections. 32 | 33 | The protocol used is set using the class variable 34 | SSL_VERSION, which must be one of ssl.PROTOCOL_* and 35 | defaults to ssl.PROTOCOL_TLSv1 for greatest security. 36 | """ 37 | SSL_VERSION = ssl.PROTOCOL_TLSv1 38 | 39 | def __init__(self, host='localhost', port=9090, validate=True, ca_certs=None, unix_socket=None): 40 | """ 41 | @param validate: Set to False to disable SSL certificate validation entirely. 42 | @type validate: bool 43 | @param ca_certs: Filename to the Certificate Authority pem file, possibly a 44 | file downloaded from: http://curl.haxx.se/ca/cacert.pem This is passed to 45 | the ssl_wrap function as the 'ca_certs' parameter. 46 | @type ca_certs: str 47 | 48 | Raises an IOError exception if validate is True and the ca_certs file is 49 | None, not present or unreadable. 50 | """ 51 | self.validate = validate 52 | self.is_valid = False 53 | self.peercert = None 54 | if not validate: 55 | self.cert_reqs = ssl.CERT_NONE 56 | else: 57 | self.cert_reqs = ssl.CERT_REQUIRED 58 | self.ca_certs = ca_certs 59 | if validate: 60 | if ca_certs is None or not os.access(ca_certs, os.R_OK): 61 | raise IOError('Certificate Authority ca_certs file "%s" is not readable, cannot validate SSL certificates.' % (ca_certs)) 62 | TSocket.TSocket.__init__(self, host, port, unix_socket) 63 | 64 | def open(self): 65 | try: 66 | res0 = self._resolveAddr() 67 | for res in res0: 68 | sock_family, sock_type= res[0:2] 69 | ip_port = res[4] 70 | plain_sock = socket.socket(sock_family, sock_type) 71 | self.handle = ssl.wrap_socket(plain_sock, ssl_version=self.SSL_VERSION, 72 | do_handshake_on_connect=True, ca_certs=self.ca_certs, cert_reqs=self.cert_reqs) 73 | self.handle.settimeout(self._timeout) 74 | try: 75 | self.handle.connect(ip_port) 76 | except socket.error, e: 77 | if res is not res0[-1]: 78 | continue 79 | else: 80 | raise e 81 | break 82 | except socket.error, e: 83 | if self._unix_socket: 84 | message = 'Could not connect to secure socket %s' % self._unix_socket 85 | else: 86 | message = 'Could not connect to %s:%d' % (self.host, self.port) 87 | raise TTransportException(type=TTransportException.NOT_OPEN, message=message) 88 | if self.validate: 89 | self._validate_cert() 90 | 91 | def _validate_cert(self): 92 | """internal method to validate the peer's SSL certificate, and to check the 93 | commonName of the certificate to ensure it matches the hostname we 94 | used to make this connection. Does not support subjectAltName records 95 | in certificates. 96 | 97 | raises TTransportException if the certificate fails validation.""" 98 | cert = self.handle.getpeercert() 99 | self.peercert = cert 100 | if 'subject' not in cert: 101 | raise TTransportException(type=TTransportException.NOT_OPEN, 102 | message='No SSL certificate found from %s:%s' % (self.host, self.port)) 103 | fields = cert['subject'] 104 | for field in fields: 105 | # ensure structure we get back is what we expect 106 | if not isinstance(field, tuple): 107 | continue 108 | cert_pair = field[0] 109 | if len(cert_pair) < 2: 110 | continue 111 | cert_key, cert_value = cert_pair[0:2] 112 | if cert_key != 'commonName': 113 | continue 114 | certhost = cert_value 115 | if certhost == self.host: 116 | # success, cert commonName matches desired hostname 117 | self.is_valid = True 118 | return 119 | else: 120 | raise TTransportException(type=TTransportException.UNKNOWN, 121 | message='Host name we connected to "%s" doesn\'t match certificate provided commonName "%s"' % (self.host, certhost)) 122 | raise TTransportException(type=TTransportException.UNKNOWN, 123 | message='Could not validate SSL certificate from host "%s". Cert=%s' % (self.host, cert)) 124 | 125 | class TSSLServerSocket(TSocket.TServerSocket): 126 | """ 127 | SSL implementation of TServerSocket 128 | 129 | This uses the ssl module's wrap_socket() method to provide SSL 130 | negotiated encryption. 131 | """ 132 | SSL_VERSION = ssl.PROTOCOL_TLSv1 133 | 134 | def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None): 135 | """Initialize a TSSLServerSocket 136 | 137 | @param certfile: The filename of the server certificate file, defaults to cert.pem 138 | @type certfile: str 139 | @param host: The hostname or IP to bind the listen socket to, i.e. 'localhost' for only allowing 140 | local network connections. Pass None to bind to all interfaces. 141 | @type host: str 142 | @param port: The port to listen on for inbound connections. 143 | @type port: int 144 | """ 145 | self.setCertfile(certfile) 146 | TSocket.TServerSocket.__init__(self, host, port) 147 | 148 | def setCertfile(self, certfile): 149 | """Set or change the server certificate file used to wrap new connections. 150 | 151 | @param certfile: The filename of the server certificate, i.e. '/etc/certs/server.pem' 152 | @type certfile: str 153 | 154 | Raises an IOError exception if the certfile is not present or unreadable. 155 | """ 156 | if not os.access(certfile, os.R_OK): 157 | raise IOError('No such certfile found: %s' % (certfile)) 158 | self.certfile = certfile 159 | 160 | def accept(self): 161 | plain_client, addr = self.handle.accept() 162 | try: 163 | client = ssl.wrap_socket(plain_client, certfile=self.certfile, 164 | server_side=True, ssl_version=self.SSL_VERSION) 165 | except ssl.SSLError, ssl_exc: 166 | # failed handshake/ssl wrap, close socket to client 167 | plain_client.close() 168 | # raise ssl_exc 169 | # We can't raise the exception, because it kills most TServer derived serve() 170 | # methods. 171 | # Instead, return None, and let the TServer instance deal with it in 172 | # other exception handling. (but TSimpleServer dies anyway) 173 | return None 174 | result = TSocket.TSocket() 175 | result.setHandle(client) 176 | return result 177 | -------------------------------------------------------------------------------- /lib/thrift/protocol/TBinaryProtocol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from TProtocol import * 21 | from struct import pack, unpack 22 | 23 | class TBinaryProtocol(TProtocolBase): 24 | 25 | """Binary implementation of the Thrift protocol driver.""" 26 | 27 | # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be 28 | # positive, converting this into a long. If we hardcode the int value 29 | # instead it'll stay in 32 bit-land. 30 | 31 | # VERSION_MASK = 0xffff0000 32 | VERSION_MASK = -65536 33 | 34 | # VERSION_1 = 0x80010000 35 | VERSION_1 = -2147418112 36 | 37 | TYPE_MASK = 0x000000ff 38 | 39 | def __init__(self, trans, strictRead=False, strictWrite=True): 40 | TProtocolBase.__init__(self, trans) 41 | self.strictRead = strictRead 42 | self.strictWrite = strictWrite 43 | 44 | def writeMessageBegin(self, name, type, seqid): 45 | if self.strictWrite: 46 | self.writeI32(TBinaryProtocol.VERSION_1 | type) 47 | self.writeString(name) 48 | self.writeI32(seqid) 49 | else: 50 | self.writeString(name) 51 | self.writeByte(type) 52 | self.writeI32(seqid) 53 | 54 | def writeMessageEnd(self): 55 | pass 56 | 57 | def writeStructBegin(self, name): 58 | pass 59 | 60 | def writeStructEnd(self): 61 | pass 62 | 63 | def writeFieldBegin(self, name, type, id): 64 | self.writeByte(type) 65 | self.writeI16(id) 66 | 67 | def writeFieldEnd(self): 68 | pass 69 | 70 | def writeFieldStop(self): 71 | self.writeByte(TType.STOP); 72 | 73 | def writeMapBegin(self, ktype, vtype, size): 74 | self.writeByte(ktype) 75 | self.writeByte(vtype) 76 | self.writeI32(size) 77 | 78 | def writeMapEnd(self): 79 | pass 80 | 81 | def writeListBegin(self, etype, size): 82 | self.writeByte(etype) 83 | self.writeI32(size) 84 | 85 | def writeListEnd(self): 86 | pass 87 | 88 | def writeSetBegin(self, etype, size): 89 | self.writeByte(etype) 90 | self.writeI32(size) 91 | 92 | def writeSetEnd(self): 93 | pass 94 | 95 | def writeBool(self, bool): 96 | if bool: 97 | self.writeByte(1) 98 | else: 99 | self.writeByte(0) 100 | 101 | def writeByte(self, byte): 102 | buff = pack("!b", byte) 103 | self.trans.write(buff) 104 | 105 | def writeI16(self, i16): 106 | buff = pack("!h", i16) 107 | self.trans.write(buff) 108 | 109 | def writeI32(self, i32): 110 | buff = pack("!i", i32) 111 | self.trans.write(buff) 112 | 113 | def writeI64(self, i64): 114 | buff = pack("!q", i64) 115 | self.trans.write(buff) 116 | 117 | def writeDouble(self, dub): 118 | buff = pack("!d", dub) 119 | self.trans.write(buff) 120 | 121 | def writeString(self, str): 122 | self.writeI32(len(str)) 123 | self.trans.write(str) 124 | 125 | def readMessageBegin(self): 126 | sz = self.readI32() 127 | if sz < 0: 128 | version = sz & TBinaryProtocol.VERSION_MASK 129 | if version != TBinaryProtocol.VERSION_1: 130 | raise TProtocolException(type=TProtocolException.BAD_VERSION, message='Bad version in readMessageBegin: %d' % (sz)) 131 | type = sz & TBinaryProtocol.TYPE_MASK 132 | name = self.readString() 133 | seqid = self.readI32() 134 | else: 135 | if self.strictRead: 136 | raise TProtocolException(type=TProtocolException.BAD_VERSION, message='No protocol version header') 137 | name = self.trans.readAll(sz) 138 | type = self.readByte() 139 | seqid = self.readI32() 140 | return (name, type, seqid) 141 | 142 | def readMessageEnd(self): 143 | pass 144 | 145 | def readStructBegin(self): 146 | pass 147 | 148 | def readStructEnd(self): 149 | pass 150 | 151 | def readFieldBegin(self): 152 | type = self.readByte() 153 | if type == TType.STOP: 154 | return (None, type, 0) 155 | id = self.readI16() 156 | return (None, type, id) 157 | 158 | def readFieldEnd(self): 159 | pass 160 | 161 | def readMapBegin(self): 162 | ktype = self.readByte() 163 | vtype = self.readByte() 164 | size = self.readI32() 165 | return (ktype, vtype, size) 166 | 167 | def readMapEnd(self): 168 | pass 169 | 170 | def readListBegin(self): 171 | etype = self.readByte() 172 | size = self.readI32() 173 | return (etype, size) 174 | 175 | def readListEnd(self): 176 | pass 177 | 178 | def readSetBegin(self): 179 | etype = self.readByte() 180 | size = self.readI32() 181 | return (etype, size) 182 | 183 | def readSetEnd(self): 184 | pass 185 | 186 | def readBool(self): 187 | byte = self.readByte() 188 | if byte == 0: 189 | return False 190 | return True 191 | 192 | def readByte(self): 193 | buff = self.trans.readAll(1) 194 | val, = unpack('!b', buff) 195 | return val 196 | 197 | def readI16(self): 198 | buff = self.trans.readAll(2) 199 | val, = unpack('!h', buff) 200 | return val 201 | 202 | def readI32(self): 203 | buff = self.trans.readAll(4) 204 | val, = unpack('!i', buff) 205 | return val 206 | 207 | def readI64(self): 208 | buff = self.trans.readAll(8) 209 | val, = unpack('!q', buff) 210 | return val 211 | 212 | def readDouble(self): 213 | buff = self.trans.readAll(8) 214 | val, = unpack('!d', buff) 215 | return val 216 | 217 | def readString(self): 218 | len = self.readI32() 219 | str = self.trans.readAll(len) 220 | return str 221 | 222 | 223 | class TBinaryProtocolFactory: 224 | def __init__(self, strictRead=False, strictWrite=True): 225 | self.strictRead = strictRead 226 | self.strictWrite = strictWrite 227 | 228 | def getProtocol(self, trans): 229 | prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite) 230 | return prot 231 | 232 | 233 | class TBinaryProtocolAccelerated(TBinaryProtocol): 234 | 235 | """C-Accelerated version of TBinaryProtocol. 236 | 237 | This class does not override any of TBinaryProtocol's methods, 238 | but the generated code recognizes it directly and will call into 239 | our C module to do the encoding, bypassing this object entirely. 240 | We inherit from TBinaryProtocol so that the normal TBinaryProtocol 241 | encoding can happen if the fastbinary module doesn't work for some 242 | reason. (TODO(dreiss): Make this happen sanely in more cases.) 243 | 244 | In order to take advantage of the C module, just use 245 | TBinaryProtocolAccelerated instead of TBinaryProtocol. 246 | 247 | NOTE: This code was contributed by an external developer. 248 | The internal Thrift team has reviewed and tested it, 249 | but we cannot guarantee that it is production-ready. 250 | Please feel free to report bugs and/or success stories 251 | to the public mailing list. 252 | """ 253 | 254 | pass 255 | 256 | 257 | class TBinaryProtocolAcceleratedFactory: 258 | def getProtocol(self, trans): 259 | return TBinaryProtocolAccelerated(trans) 260 | -------------------------------------------------------------------------------- /lib/thrift/transport/TZlibTransport.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | ''' 20 | TZlibTransport provides a compressed transport and transport factory 21 | class, using the python standard library zlib module to implement 22 | data compression. 23 | ''' 24 | 25 | from __future__ import division 26 | import zlib 27 | from cStringIO import StringIO 28 | from TTransport import TTransportBase, CReadableTransport 29 | 30 | class TZlibTransportFactory(object): 31 | ''' 32 | Factory transport that builds zlib compressed transports. 33 | 34 | This factory caches the last single client/transport that it was passed 35 | and returns the same TZlibTransport object that was created. 36 | 37 | This caching means the TServer class will get the _same_ transport 38 | object for both input and output transports from this factory. 39 | (For non-threaded scenarios only, since the cache only holds one object) 40 | 41 | The purpose of this caching is to allocate only one TZlibTransport where 42 | only one is really needed (since it must have separate read/write buffers), 43 | and makes the statistics from getCompSavings() and getCompRatio() 44 | easier to understand. 45 | ''' 46 | 47 | # class scoped cache of last transport given and zlibtransport returned 48 | _last_trans = None 49 | _last_z = None 50 | 51 | def getTransport(self, trans, compresslevel=9): 52 | '''Wrap a transport , trans, with the TZlibTransport 53 | compressed transport class, returning a new 54 | transport to the caller. 55 | 56 | @param compresslevel: The zlib compression level, ranging 57 | from 0 (no compression) to 9 (best compression). Defaults to 9. 58 | @type compresslevel: int 59 | 60 | This method returns a TZlibTransport which wraps the 61 | passed C{trans} TTransport derived instance. 62 | ''' 63 | if trans == self._last_trans: 64 | return self._last_z 65 | ztrans = TZlibTransport(trans, compresslevel) 66 | self._last_trans = trans 67 | self._last_z = ztrans 68 | return ztrans 69 | 70 | 71 | class TZlibTransport(TTransportBase, CReadableTransport): 72 | ''' 73 | Class that wraps a transport with zlib, compressing writes 74 | and decompresses reads, using the python standard 75 | library zlib module. 76 | ''' 77 | 78 | # Read buffer size for the python fastbinary C extension, 79 | # the TBinaryProtocolAccelerated class. 80 | DEFAULT_BUFFSIZE = 4096 81 | 82 | def __init__(self, trans, compresslevel=9): 83 | ''' 84 | Create a new TZlibTransport, wrapping C{trans}, another 85 | TTransport derived object. 86 | 87 | @param trans: A thrift transport object, i.e. a TSocket() object. 88 | @type trans: TTransport 89 | @param compresslevel: The zlib compression level, ranging 90 | from 0 (no compression) to 9 (best compression). Default is 9. 91 | @type compresslevel: int 92 | ''' 93 | self.__trans = trans 94 | self.compresslevel = compresslevel 95 | self.__rbuf = StringIO() 96 | self.__wbuf = StringIO() 97 | self._init_zlib() 98 | self._init_stats() 99 | 100 | def _reinit_buffers(self): 101 | ''' 102 | Internal method to initialize/reset the internal StringIO objects 103 | for read and write buffers. 104 | ''' 105 | self.__rbuf = StringIO() 106 | self.__wbuf = StringIO() 107 | 108 | def _init_stats(self): 109 | ''' 110 | Internal method to reset the internal statistics counters 111 | for compression ratios and bandwidth savings. 112 | ''' 113 | self.bytes_in = 0 114 | self.bytes_out = 0 115 | self.bytes_in_comp = 0 116 | self.bytes_out_comp = 0 117 | 118 | def _init_zlib(self): 119 | ''' 120 | Internal method for setting up the zlib compression and 121 | decompression objects. 122 | ''' 123 | self._zcomp_read = zlib.decompressobj() 124 | self._zcomp_write = zlib.compressobj(self.compresslevel) 125 | 126 | def getCompRatio(self): 127 | ''' 128 | Get the current measured compression ratios (in,out) from 129 | this transport. 130 | 131 | Returns a tuple of: 132 | (inbound_compression_ratio, outbound_compression_ratio) 133 | 134 | The compression ratios are computed as: 135 | compressed / uncompressed 136 | 137 | E.g., data that compresses by 10x will have a ratio of: 0.10 138 | and data that compresses to half of ts original size will 139 | have a ratio of 0.5 140 | 141 | None is returned if no bytes have yet been processed in 142 | a particular direction. 143 | ''' 144 | r_percent, w_percent = (None, None) 145 | if self.bytes_in > 0: 146 | r_percent = self.bytes_in_comp / self.bytes_in 147 | if self.bytes_out > 0: 148 | w_percent = self.bytes_out_comp / self.bytes_out 149 | return (r_percent, w_percent) 150 | 151 | def getCompSavings(self): 152 | ''' 153 | Get the current count of saved bytes due to data 154 | compression. 155 | 156 | Returns a tuple of: 157 | (inbound_saved_bytes, outbound_saved_bytes) 158 | 159 | Note: if compression is actually expanding your 160 | data (only likely with very tiny thrift objects), then 161 | the values returned will be negative. 162 | ''' 163 | r_saved = self.bytes_in - self.bytes_in_comp 164 | w_saved = self.bytes_out - self.bytes_out_comp 165 | return (r_saved, w_saved) 166 | 167 | def isOpen(self): 168 | '''Return the underlying transport's open status''' 169 | return self.__trans.isOpen() 170 | 171 | def open(self): 172 | """Open the underlying transport""" 173 | self._init_stats() 174 | return self.__trans.open() 175 | 176 | def listen(self): 177 | '''Invoke the underlying transport's listen() method''' 178 | self.__trans.listen() 179 | 180 | def accept(self): 181 | '''Accept connections on the underlying transport''' 182 | return self.__trans.accept() 183 | 184 | def close(self): 185 | '''Close the underlying transport,''' 186 | self._reinit_buffers() 187 | self._init_zlib() 188 | return self.__trans.close() 189 | 190 | def read(self, sz): 191 | ''' 192 | Read up to sz bytes from the decompressed bytes buffer, and 193 | read from the underlying transport if the decompression 194 | buffer is empty. 195 | ''' 196 | ret = self.__rbuf.read(sz) 197 | if len(ret) > 0: 198 | return ret 199 | # keep reading from transport until something comes back 200 | while True: 201 | if self.readComp(sz): 202 | break 203 | ret = self.__rbuf.read(sz) 204 | return ret 205 | 206 | def readComp(self, sz): 207 | ''' 208 | Read compressed data from the underlying transport, then 209 | decompress it and append it to the internal StringIO read buffer 210 | ''' 211 | zbuf = self.__trans.read(sz) 212 | zbuf = self._zcomp_read.unconsumed_tail + zbuf 213 | buf = self._zcomp_read.decompress(zbuf) 214 | self.bytes_in += len(zbuf) 215 | self.bytes_in_comp += len(buf) 216 | old = self.__rbuf.read() 217 | self.__rbuf = StringIO(old + buf) 218 | if len(old) + len(buf) == 0: 219 | return False 220 | return True 221 | 222 | def write(self, buf): 223 | ''' 224 | Write some bytes, putting them into the internal write 225 | buffer for eventual compression. 226 | ''' 227 | self.__wbuf.write(buf) 228 | 229 | def flush(self): 230 | ''' 231 | Flush any queued up data in the write buffer and ensure the 232 | compression buffer is flushed out to the underlying transport 233 | ''' 234 | wout = self.__wbuf.getvalue() 235 | if len(wout) > 0: 236 | zbuf = self._zcomp_write.compress(wout) 237 | self.bytes_out += len(wout) 238 | self.bytes_out_comp += len(zbuf) 239 | else: 240 | zbuf = '' 241 | ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH) 242 | self.bytes_out_comp += len(ztail) 243 | if (len(zbuf) + len(ztail)) > 0: 244 | self.__wbuf = StringIO() 245 | self.__trans.write(zbuf + ztail) 246 | self.__trans.flush() 247 | 248 | @property 249 | def cstringio_buf(self): 250 | '''Implement the CReadableTransport interface''' 251 | return self.__rbuf 252 | 253 | def cstringio_refill(self, partialread, reqlen): 254 | '''Implement the CReadableTransport interface for refill''' 255 | retstring = partialread 256 | if reqlen < self.DEFAULT_BUFFSIZE: 257 | retstring += self.read(self.DEFAULT_BUFFSIZE) 258 | while len(retstring) < reqlen: 259 | retstring += self.read(reqlen - len(retstring)) 260 | self.__rbuf = StringIO(retstring) 261 | return self.__rbuf 262 | -------------------------------------------------------------------------------- /lib/thrift/server/TServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import logging 21 | import sys 22 | import os 23 | import traceback 24 | import threading 25 | import Queue 26 | 27 | from thrift.Thrift import TProcessor 28 | from thrift.transport import TTransport 29 | from thrift.protocol import TBinaryProtocol 30 | 31 | class TServer: 32 | 33 | """Base interface for a server, which must have a serve method.""" 34 | 35 | """ 3 constructors for all servers: 36 | 1) (processor, serverTransport) 37 | 2) (processor, serverTransport, transportFactory, protocolFactory) 38 | 3) (processor, serverTransport, 39 | inputTransportFactory, outputTransportFactory, 40 | inputProtocolFactory, outputProtocolFactory)""" 41 | def __init__(self, *args): 42 | if (len(args) == 2): 43 | self.__initArgs__(args[0], args[1], 44 | TTransport.TTransportFactoryBase(), 45 | TTransport.TTransportFactoryBase(), 46 | TBinaryProtocol.TBinaryProtocolFactory(), 47 | TBinaryProtocol.TBinaryProtocolFactory()) 48 | elif (len(args) == 4): 49 | self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3]) 50 | elif (len(args) == 6): 51 | self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5]) 52 | 53 | def __initArgs__(self, processor, serverTransport, 54 | inputTransportFactory, outputTransportFactory, 55 | inputProtocolFactory, outputProtocolFactory): 56 | self.processor = processor 57 | self.serverTransport = serverTransport 58 | self.inputTransportFactory = inputTransportFactory 59 | self.outputTransportFactory = outputTransportFactory 60 | self.inputProtocolFactory = inputProtocolFactory 61 | self.outputProtocolFactory = outputProtocolFactory 62 | 63 | def serve(self): 64 | pass 65 | 66 | class TSimpleServer(TServer): 67 | 68 | """Simple single-threaded server that just pumps around one transport.""" 69 | 70 | def __init__(self, *args): 71 | TServer.__init__(self, *args) 72 | 73 | def serve(self): 74 | self.serverTransport.listen() 75 | while True: 76 | client = self.serverTransport.accept() 77 | itrans = self.inputTransportFactory.getTransport(client) 78 | otrans = self.outputTransportFactory.getTransport(client) 79 | iprot = self.inputProtocolFactory.getProtocol(itrans) 80 | oprot = self.outputProtocolFactory.getProtocol(otrans) 81 | try: 82 | while True: 83 | self.processor.process(iprot, oprot) 84 | except TTransport.TTransportException, tx: 85 | pass 86 | except Exception, x: 87 | logging.exception(x) 88 | 89 | itrans.close() 90 | otrans.close() 91 | 92 | class TThreadedServer(TServer): 93 | 94 | """Threaded server that spawns a new thread per each connection.""" 95 | 96 | def __init__(self, *args, **kwargs): 97 | TServer.__init__(self, *args) 98 | self.daemon = kwargs.get("daemon", False) 99 | 100 | def serve(self): 101 | self.serverTransport.listen() 102 | while True: 103 | try: 104 | client = self.serverTransport.accept() 105 | t = threading.Thread(target = self.handle, args=(client,)) 106 | t.setDaemon(self.daemon) 107 | t.start() 108 | except KeyboardInterrupt: 109 | raise 110 | except Exception, x: 111 | logging.exception(x) 112 | 113 | def handle(self, client): 114 | itrans = self.inputTransportFactory.getTransport(client) 115 | otrans = self.outputTransportFactory.getTransport(client) 116 | iprot = self.inputProtocolFactory.getProtocol(itrans) 117 | oprot = self.outputProtocolFactory.getProtocol(otrans) 118 | try: 119 | while True: 120 | self.processor.process(iprot, oprot) 121 | except TTransport.TTransportException, tx: 122 | pass 123 | except Exception, x: 124 | logging.exception(x) 125 | 126 | itrans.close() 127 | otrans.close() 128 | 129 | class TThreadPoolServer(TServer): 130 | 131 | """Server with a fixed size pool of threads which service requests.""" 132 | 133 | def __init__(self, *args, **kwargs): 134 | TServer.__init__(self, *args) 135 | self.clients = Queue.Queue() 136 | self.threads = 10 137 | self.daemon = kwargs.get("daemon", False) 138 | 139 | def setNumThreads(self, num): 140 | """Set the number of worker threads that should be created""" 141 | self.threads = num 142 | 143 | def serveThread(self): 144 | """Loop around getting clients from the shared queue and process them.""" 145 | while True: 146 | try: 147 | client = self.clients.get() 148 | self.serveClient(client) 149 | except Exception, x: 150 | logging.exception(x) 151 | 152 | def serveClient(self, client): 153 | """Process input/output from a client for as long as possible""" 154 | itrans = self.inputTransportFactory.getTransport(client) 155 | otrans = self.outputTransportFactory.getTransport(client) 156 | iprot = self.inputProtocolFactory.getProtocol(itrans) 157 | oprot = self.outputProtocolFactory.getProtocol(otrans) 158 | try: 159 | while True: 160 | self.processor.process(iprot, oprot) 161 | except TTransport.TTransportException, tx: 162 | pass 163 | except Exception, x: 164 | logging.exception(x) 165 | 166 | itrans.close() 167 | otrans.close() 168 | 169 | def serve(self): 170 | """Start a fixed number of worker threads and put client into a queue""" 171 | for i in range(self.threads): 172 | try: 173 | t = threading.Thread(target = self.serveThread) 174 | t.setDaemon(self.daemon) 175 | t.start() 176 | except Exception, x: 177 | logging.exception(x) 178 | 179 | # Pump the socket for clients 180 | self.serverTransport.listen() 181 | while True: 182 | try: 183 | client = self.serverTransport.accept() 184 | self.clients.put(client) 185 | except Exception, x: 186 | logging.exception(x) 187 | 188 | 189 | class TForkingServer(TServer): 190 | 191 | """A Thrift server that forks a new process for each request""" 192 | """ 193 | This is more scalable than the threaded server as it does not cause 194 | GIL contention. 195 | 196 | Note that this has different semantics from the threading server. 197 | Specifically, updates to shared variables will no longer be shared. 198 | It will also not work on windows. 199 | 200 | This code is heavily inspired by SocketServer.ForkingMixIn in the 201 | Python stdlib. 202 | """ 203 | 204 | def __init__(self, *args): 205 | TServer.__init__(self, *args) 206 | self.children = [] 207 | 208 | def serve(self): 209 | def try_close(file): 210 | try: 211 | file.close() 212 | except IOError, e: 213 | logging.warning(e, exc_info=True) 214 | 215 | 216 | self.serverTransport.listen() 217 | while True: 218 | client = self.serverTransport.accept() 219 | try: 220 | pid = os.fork() 221 | 222 | if pid: # parent 223 | # add before collect, otherwise you race w/ waitpid 224 | self.children.append(pid) 225 | self.collect_children() 226 | 227 | # Parent must close socket or the connection may not get 228 | # closed promptly 229 | itrans = self.inputTransportFactory.getTransport(client) 230 | otrans = self.outputTransportFactory.getTransport(client) 231 | try_close(itrans) 232 | try_close(otrans) 233 | else: 234 | itrans = self.inputTransportFactory.getTransport(client) 235 | otrans = self.outputTransportFactory.getTransport(client) 236 | 237 | iprot = self.inputProtocolFactory.getProtocol(itrans) 238 | oprot = self.outputProtocolFactory.getProtocol(otrans) 239 | 240 | ecode = 0 241 | try: 242 | try: 243 | while True: 244 | self.processor.process(iprot, oprot) 245 | except TTransport.TTransportException, tx: 246 | pass 247 | except Exception, e: 248 | logging.exception(e) 249 | ecode = 1 250 | finally: 251 | try_close(itrans) 252 | try_close(otrans) 253 | 254 | os._exit(ecode) 255 | 256 | except TTransport.TTransportException, tx: 257 | pass 258 | except Exception, x: 259 | logging.exception(x) 260 | 261 | 262 | def collect_children(self): 263 | while self.children: 264 | try: 265 | pid, status = os.waitpid(0, os.WNOHANG) 266 | except os.error: 267 | pid = None 268 | 269 | if pid: 270 | self.children.remove(pid) 271 | else: 272 | break 273 | 274 | 275 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | -------------------------------------------------------------------------------- /lib/thrift/transport/TTransport.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from cStringIO import StringIO 21 | from struct import pack,unpack 22 | from thrift.Thrift import TException 23 | 24 | class TTransportException(TException): 25 | 26 | """Custom Transport Exception class""" 27 | 28 | UNKNOWN = 0 29 | NOT_OPEN = 1 30 | ALREADY_OPEN = 2 31 | TIMED_OUT = 3 32 | END_OF_FILE = 4 33 | 34 | def __init__(self, type=UNKNOWN, message=None): 35 | TException.__init__(self, message) 36 | self.type = type 37 | 38 | class TTransportBase: 39 | 40 | """Base class for Thrift transport layer.""" 41 | 42 | def isOpen(self): 43 | pass 44 | 45 | def open(self): 46 | pass 47 | 48 | def close(self): 49 | pass 50 | 51 | def read(self, sz): 52 | pass 53 | 54 | def readAll(self, sz): 55 | buff = '' 56 | have = 0 57 | while (have < sz): 58 | chunk = self.read(sz-have) 59 | have += len(chunk) 60 | buff += chunk 61 | 62 | if len(chunk) == 0: 63 | raise EOFError() 64 | 65 | return buff 66 | 67 | def write(self, buf): 68 | pass 69 | 70 | def flush(self): 71 | pass 72 | 73 | # This class should be thought of as an interface. 74 | class CReadableTransport: 75 | """base class for transports that are readable from C""" 76 | 77 | # TODO(dreiss): Think about changing this interface to allow us to use 78 | # a (Python, not c) StringIO instead, because it allows 79 | # you to write after reading. 80 | 81 | # NOTE: This is a classic class, so properties will NOT work 82 | # correctly for setting. 83 | @property 84 | def cstringio_buf(self): 85 | """A cStringIO buffer that contains the current chunk we are reading.""" 86 | pass 87 | 88 | def cstringio_refill(self, partialread, reqlen): 89 | """Refills cstringio_buf. 90 | 91 | Returns the currently used buffer (which can but need not be the same as 92 | the old cstringio_buf). partialread is what the C code has read from the 93 | buffer, and should be inserted into the buffer before any more reads. The 94 | return value must be a new, not borrowed reference. Something along the 95 | lines of self._buf should be fine. 96 | 97 | If reqlen bytes can't be read, throw EOFError. 98 | """ 99 | pass 100 | 101 | class TServerTransportBase: 102 | 103 | """Base class for Thrift server transports.""" 104 | 105 | def listen(self): 106 | pass 107 | 108 | def accept(self): 109 | pass 110 | 111 | def close(self): 112 | pass 113 | 114 | class TTransportFactoryBase: 115 | 116 | """Base class for a Transport Factory""" 117 | 118 | def getTransport(self, trans): 119 | return trans 120 | 121 | class TBufferedTransportFactory: 122 | 123 | """Factory transport that builds buffered transports""" 124 | 125 | def getTransport(self, trans): 126 | buffered = TBufferedTransport(trans) 127 | return buffered 128 | 129 | 130 | class TBufferedTransport(TTransportBase,CReadableTransport): 131 | 132 | """Class that wraps another transport and buffers its I/O. 133 | 134 | The implementation uses a (configurable) fixed-size read buffer 135 | but buffers all writes until a flush is performed. 136 | """ 137 | 138 | DEFAULT_BUFFER = 4096 139 | 140 | def __init__(self, trans, rbuf_size = DEFAULT_BUFFER): 141 | self.__trans = trans 142 | self.__wbuf = StringIO() 143 | self.__rbuf = StringIO("") 144 | self.__rbuf_size = rbuf_size 145 | 146 | def isOpen(self): 147 | return self.__trans.isOpen() 148 | 149 | def open(self): 150 | return self.__trans.open() 151 | 152 | def close(self): 153 | return self.__trans.close() 154 | 155 | def read(self, sz): 156 | ret = self.__rbuf.read(sz) 157 | if len(ret) != 0: 158 | return ret 159 | 160 | self.__rbuf = StringIO(self.__trans.read(max(sz, self.__rbuf_size))) 161 | return self.__rbuf.read(sz) 162 | 163 | def write(self, buf): 164 | self.__wbuf.write(buf) 165 | 166 | def flush(self): 167 | out = self.__wbuf.getvalue() 168 | # reset wbuf before write/flush to preserve state on underlying failure 169 | self.__wbuf = StringIO() 170 | self.__trans.write(out) 171 | self.__trans.flush() 172 | 173 | # Implement the CReadableTransport interface. 174 | @property 175 | def cstringio_buf(self): 176 | return self.__rbuf 177 | 178 | def cstringio_refill(self, partialread, reqlen): 179 | retstring = partialread 180 | if reqlen < self.__rbuf_size: 181 | # try to make a read of as much as we can. 182 | retstring += self.__trans.read(self.__rbuf_size) 183 | 184 | # but make sure we do read reqlen bytes. 185 | if len(retstring) < reqlen: 186 | retstring += self.__trans.readAll(reqlen - len(retstring)) 187 | 188 | self.__rbuf = StringIO(retstring) 189 | return self.__rbuf 190 | 191 | class TMemoryBuffer(TTransportBase, CReadableTransport): 192 | """Wraps a cStringIO object as a TTransport. 193 | 194 | NOTE: Unlike the C++ version of this class, you cannot write to it 195 | then immediately read from it. If you want to read from a 196 | TMemoryBuffer, you must either pass a string to the constructor. 197 | TODO(dreiss): Make this work like the C++ version. 198 | """ 199 | 200 | def __init__(self, value=None): 201 | """value -- a value to read from for stringio 202 | 203 | If value is set, this will be a transport for reading, 204 | otherwise, it is for writing""" 205 | if value is not None: 206 | self._buffer = StringIO(value) 207 | else: 208 | self._buffer = StringIO() 209 | 210 | def isOpen(self): 211 | return not self._buffer.closed 212 | 213 | def open(self): 214 | pass 215 | 216 | def close(self): 217 | self._buffer.close() 218 | 219 | def read(self, sz): 220 | return self._buffer.read(sz) 221 | 222 | def write(self, buf): 223 | self._buffer.write(buf) 224 | 225 | def flush(self): 226 | pass 227 | 228 | def getvalue(self): 229 | return self._buffer.getvalue() 230 | 231 | # Implement the CReadableTransport interface. 232 | @property 233 | def cstringio_buf(self): 234 | return self._buffer 235 | 236 | def cstringio_refill(self, partialread, reqlen): 237 | # only one shot at reading... 238 | raise EOFError() 239 | 240 | class TFramedTransportFactory: 241 | 242 | """Factory transport that builds framed transports""" 243 | 244 | def getTransport(self, trans): 245 | framed = TFramedTransport(trans) 246 | return framed 247 | 248 | 249 | class TFramedTransport(TTransportBase, CReadableTransport): 250 | 251 | """Class that wraps another transport and frames its I/O when writing.""" 252 | 253 | def __init__(self, trans,): 254 | self.__trans = trans 255 | self.__rbuf = StringIO() 256 | self.__wbuf = StringIO() 257 | 258 | def isOpen(self): 259 | return self.__trans.isOpen() 260 | 261 | def open(self): 262 | return self.__trans.open() 263 | 264 | def close(self): 265 | return self.__trans.close() 266 | 267 | def read(self, sz): 268 | ret = self.__rbuf.read(sz) 269 | if len(ret) != 0: 270 | return ret 271 | 272 | self.readFrame() 273 | return self.__rbuf.read(sz) 274 | 275 | def readFrame(self): 276 | buff = self.__trans.readAll(4) 277 | sz, = unpack('!i', buff) 278 | self.__rbuf = StringIO(self.__trans.readAll(sz)) 279 | 280 | def write(self, buf): 281 | self.__wbuf.write(buf) 282 | 283 | def flush(self): 284 | wout = self.__wbuf.getvalue() 285 | wsz = len(wout) 286 | # reset wbuf before write/flush to preserve state on underlying failure 287 | self.__wbuf = StringIO() 288 | # N.B.: Doing this string concatenation is WAY cheaper than making 289 | # two separate calls to the underlying socket object. Socket writes in 290 | # Python turn out to be REALLY expensive, but it seems to do a pretty 291 | # good job of managing string buffer operations without excessive copies 292 | buf = pack("!i", wsz) + wout 293 | self.__trans.write(buf) 294 | self.__trans.flush() 295 | 296 | # Implement the CReadableTransport interface. 297 | @property 298 | def cstringio_buf(self): 299 | return self.__rbuf 300 | 301 | def cstringio_refill(self, prefix, reqlen): 302 | # self.__rbuf will already be empty here because fastbinary doesn't 303 | # ask for a refill until the previous buffer is empty. Therefore, 304 | # we can start reading new frames immediately. 305 | while len(prefix) < reqlen: 306 | self.readFrame() 307 | prefix += self.__rbuf.getvalue() 308 | self.__rbuf = StringIO(prefix) 309 | return self.__rbuf 310 | 311 | 312 | class TFileObjectTransport(TTransportBase): 313 | """Wraps a file-like object to make it work as a Thrift transport.""" 314 | 315 | def __init__(self, fileobj): 316 | self.fileobj = fileobj 317 | 318 | def isOpen(self): 319 | return True 320 | 321 | def close(self): 322 | self.fileobj.close() 323 | 324 | def read(self, sz): 325 | return self.fileobj.read(sz) 326 | 327 | def write(self, buf): 328 | self.fileobj.write(buf) 329 | 330 | def flush(self): 331 | self.fileobj.flush() 332 | -------------------------------------------------------------------------------- /lib/APACHE-LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /lib/thrift/server/TNonblockingServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | """Implementation of non-blocking server. 20 | 21 | The main idea of the server is reciving and sending requests 22 | only from main thread. 23 | 24 | It also makes thread pool server in tasks terms, not connections. 25 | """ 26 | import threading 27 | import socket 28 | import Queue 29 | import select 30 | import struct 31 | import logging 32 | 33 | from thrift.transport import TTransport 34 | from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory 35 | 36 | __all__ = ['TNonblockingServer'] 37 | 38 | class Worker(threading.Thread): 39 | """Worker is a small helper to process incoming connection.""" 40 | def __init__(self, queue): 41 | threading.Thread.__init__(self) 42 | self.queue = queue 43 | 44 | def run(self): 45 | """Process queries from task queue, stop if processor is None.""" 46 | while True: 47 | try: 48 | processor, iprot, oprot, otrans, callback = self.queue.get() 49 | if processor is None: 50 | break 51 | processor.process(iprot, oprot) 52 | callback(True, otrans.getvalue()) 53 | except Exception: 54 | logging.exception("Exception while processing request") 55 | callback(False, '') 56 | 57 | WAIT_LEN = 0 58 | WAIT_MESSAGE = 1 59 | WAIT_PROCESS = 2 60 | SEND_ANSWER = 3 61 | CLOSED = 4 62 | 63 | def locked(func): 64 | "Decorator which locks self.lock." 65 | def nested(self, *args, **kwargs): 66 | self.lock.acquire() 67 | try: 68 | return func(self, *args, **kwargs) 69 | finally: 70 | self.lock.release() 71 | return nested 72 | 73 | def socket_exception(func): 74 | "Decorator close object on socket.error." 75 | def read(self, *args, **kwargs): 76 | try: 77 | return func(self, *args, **kwargs) 78 | except socket.error: 79 | self.close() 80 | return read 81 | 82 | class Connection: 83 | """Basic class is represented connection. 84 | 85 | It can be in state: 86 | WAIT_LEN --- connection is reading request len. 87 | WAIT_MESSAGE --- connection is reading request. 88 | WAIT_PROCESS --- connection has just read whole request and 89 | waits for call ready routine. 90 | SEND_ANSWER --- connection is sending answer string (including length 91 | of answer). 92 | CLOSED --- socket was closed and connection should be deleted. 93 | """ 94 | def __init__(self, new_socket, wake_up): 95 | self.socket = new_socket 96 | self.socket.setblocking(False) 97 | self.status = WAIT_LEN 98 | self.len = 0 99 | self.message = '' 100 | self.lock = threading.Lock() 101 | self.wake_up = wake_up 102 | 103 | def _read_len(self): 104 | """Reads length of request. 105 | 106 | It's really paranoic routine and it may be replaced by 107 | self.socket.recv(4).""" 108 | read = self.socket.recv(4 - len(self.message)) 109 | if len(read) == 0: 110 | # if we read 0 bytes and self.message is empty, it means client close 111 | # connection 112 | if len(self.message) != 0: 113 | logging.error("can't read frame size from socket") 114 | self.close() 115 | return 116 | self.message += read 117 | if len(self.message) == 4: 118 | self.len, = struct.unpack('!i', self.message) 119 | if self.len < 0: 120 | logging.error("negative frame size, it seems client"\ 121 | " doesn't use FramedTransport") 122 | self.close() 123 | elif self.len == 0: 124 | logging.error("empty frame, it's really strange") 125 | self.close() 126 | else: 127 | self.message = '' 128 | self.status = WAIT_MESSAGE 129 | 130 | @socket_exception 131 | def read(self): 132 | """Reads data from stream and switch state.""" 133 | assert self.status in (WAIT_LEN, WAIT_MESSAGE) 134 | if self.status == WAIT_LEN: 135 | self._read_len() 136 | # go back to the main loop here for simplicity instead of 137 | # falling through, even though there is a good chance that 138 | # the message is already available 139 | elif self.status == WAIT_MESSAGE: 140 | read = self.socket.recv(self.len - len(self.message)) 141 | if len(read) == 0: 142 | logging.error("can't read frame from socket (get %d of %d bytes)" % 143 | (len(self.message), self.len)) 144 | self.close() 145 | return 146 | self.message += read 147 | if len(self.message) == self.len: 148 | self.status = WAIT_PROCESS 149 | 150 | @socket_exception 151 | def write(self): 152 | """Writes data from socket and switch state.""" 153 | assert self.status == SEND_ANSWER 154 | sent = self.socket.send(self.message) 155 | if sent == len(self.message): 156 | self.status = WAIT_LEN 157 | self.message = '' 158 | self.len = 0 159 | else: 160 | self.message = self.message[sent:] 161 | 162 | @locked 163 | def ready(self, all_ok, message): 164 | """Callback function for switching state and waking up main thread. 165 | 166 | This function is the only function witch can be called asynchronous. 167 | 168 | The ready can switch Connection to three states: 169 | WAIT_LEN if request was oneway. 170 | SEND_ANSWER if request was processed in normal way. 171 | CLOSED if request throws unexpected exception. 172 | 173 | The one wakes up main thread. 174 | """ 175 | assert self.status == WAIT_PROCESS 176 | if not all_ok: 177 | self.close() 178 | self.wake_up() 179 | return 180 | self.len = '' 181 | if len(message) == 0: 182 | # it was a oneway request, do not write answer 183 | self.message = '' 184 | self.status = WAIT_LEN 185 | else: 186 | self.message = struct.pack('!i', len(message)) + message 187 | self.status = SEND_ANSWER 188 | self.wake_up() 189 | 190 | @locked 191 | def is_writeable(self): 192 | "Returns True if connection should be added to write list of select." 193 | return self.status == SEND_ANSWER 194 | 195 | # it's not necessary, but... 196 | @locked 197 | def is_readable(self): 198 | "Returns True if connection should be added to read list of select." 199 | return self.status in (WAIT_LEN, WAIT_MESSAGE) 200 | 201 | @locked 202 | def is_closed(self): 203 | "Returns True if connection is closed." 204 | return self.status == CLOSED 205 | 206 | def fileno(self): 207 | "Returns the file descriptor of the associated socket." 208 | return self.socket.fileno() 209 | 210 | def close(self): 211 | "Closes connection" 212 | self.status = CLOSED 213 | self.socket.close() 214 | 215 | class TNonblockingServer: 216 | """Non-blocking server.""" 217 | def __init__(self, processor, lsocket, inputProtocolFactory=None, 218 | outputProtocolFactory=None, threads=10): 219 | self.processor = processor 220 | self.socket = lsocket 221 | self.in_protocol = inputProtocolFactory or TBinaryProtocolFactory() 222 | self.out_protocol = outputProtocolFactory or self.in_protocol 223 | self.threads = int(threads) 224 | self.clients = {} 225 | self.tasks = Queue.Queue() 226 | self._read, self._write = socket.socketpair() 227 | self.prepared = False 228 | 229 | def setNumThreads(self, num): 230 | """Set the number of worker threads that should be created.""" 231 | # implement ThreadPool interface 232 | assert not self.prepared, "You can't change number of threads for working server" 233 | self.threads = num 234 | 235 | def prepare(self): 236 | """Prepares server for serve requests.""" 237 | self.socket.listen() 238 | for _ in xrange(self.threads): 239 | thread = Worker(self.tasks) 240 | thread.setDaemon(True) 241 | thread.start() 242 | self.prepared = True 243 | 244 | def wake_up(self): 245 | """Wake up main thread. 246 | 247 | The server usualy waits in select call in we should terminate one. 248 | The simplest way is using socketpair. 249 | 250 | Select always wait to read from the first socket of socketpair. 251 | 252 | In this case, we can just write anything to the second socket from 253 | socketpair.""" 254 | self._write.send('1') 255 | 256 | def _select(self): 257 | """Does select on open connections.""" 258 | readable = [self.socket.handle.fileno(), self._read.fileno()] 259 | writable = [] 260 | for i, connection in self.clients.items(): 261 | if connection.is_readable(): 262 | readable.append(connection.fileno()) 263 | if connection.is_writeable(): 264 | writable.append(connection.fileno()) 265 | if connection.is_closed(): 266 | del self.clients[i] 267 | return select.select(readable, writable, readable) 268 | 269 | def handle(self): 270 | """Handle requests. 271 | 272 | WARNING! You must call prepare BEFORE calling handle. 273 | """ 274 | assert self.prepared, "You have to call prepare before handle" 275 | rset, wset, xset = self._select() 276 | for readable in rset: 277 | if readable == self._read.fileno(): 278 | # don't care i just need to clean readable flag 279 | self._read.recv(1024) 280 | elif readable == self.socket.handle.fileno(): 281 | client = self.socket.accept().handle 282 | self.clients[client.fileno()] = Connection(client, self.wake_up) 283 | else: 284 | connection = self.clients[readable] 285 | connection.read() 286 | if connection.status == WAIT_PROCESS: 287 | itransport = TTransport.TMemoryBuffer(connection.message) 288 | otransport = TTransport.TMemoryBuffer() 289 | iprot = self.in_protocol.getProtocol(itransport) 290 | oprot = self.out_protocol.getProtocol(otransport) 291 | self.tasks.put([self.processor, iprot, oprot, 292 | otransport, connection.ready]) 293 | for writeable in wset: 294 | self.clients[writeable].write() 295 | for oob in xset: 296 | self.clients[oob].close() 297 | del self.clients[oob] 298 | 299 | def close(self): 300 | """Closes the server.""" 301 | for _ in xrange(self.threads): 302 | self.tasks.put([None, None, None, None, None]) 303 | self.socket.close() 304 | self.prepared = False 305 | 306 | def serve(self): 307 | """Serve forever.""" 308 | self.prepare() 309 | while True: 310 | self.handle() 311 | -------------------------------------------------------------------------------- /lib/thrift/protocol/TProtocol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from thrift.Thrift import * 21 | 22 | class TProtocolException(TException): 23 | 24 | """Custom Protocol Exception class""" 25 | 26 | UNKNOWN = 0 27 | INVALID_DATA = 1 28 | NEGATIVE_SIZE = 2 29 | SIZE_LIMIT = 3 30 | BAD_VERSION = 4 31 | 32 | def __init__(self, type=UNKNOWN, message=None): 33 | TException.__init__(self, message) 34 | self.type = type 35 | 36 | class TProtocolBase: 37 | 38 | """Base class for Thrift protocol driver.""" 39 | 40 | def __init__(self, trans): 41 | self.trans = trans 42 | 43 | def writeMessageBegin(self, name, type, seqid): 44 | pass 45 | 46 | def writeMessageEnd(self): 47 | pass 48 | 49 | def writeStructBegin(self, name): 50 | pass 51 | 52 | def writeStructEnd(self): 53 | pass 54 | 55 | def writeFieldBegin(self, name, type, id): 56 | pass 57 | 58 | def writeFieldEnd(self): 59 | pass 60 | 61 | def writeFieldStop(self): 62 | pass 63 | 64 | def writeMapBegin(self, ktype, vtype, size): 65 | pass 66 | 67 | def writeMapEnd(self): 68 | pass 69 | 70 | def writeListBegin(self, etype, size): 71 | pass 72 | 73 | def writeListEnd(self): 74 | pass 75 | 76 | def writeSetBegin(self, etype, size): 77 | pass 78 | 79 | def writeSetEnd(self): 80 | pass 81 | 82 | def writeBool(self, bool): 83 | pass 84 | 85 | def writeByte(self, byte): 86 | pass 87 | 88 | def writeI16(self, i16): 89 | pass 90 | 91 | def writeI32(self, i32): 92 | pass 93 | 94 | def writeI64(self, i64): 95 | pass 96 | 97 | def writeDouble(self, dub): 98 | pass 99 | 100 | def writeString(self, str): 101 | pass 102 | 103 | def readMessageBegin(self): 104 | pass 105 | 106 | def readMessageEnd(self): 107 | pass 108 | 109 | def readStructBegin(self): 110 | pass 111 | 112 | def readStructEnd(self): 113 | pass 114 | 115 | def readFieldBegin(self): 116 | pass 117 | 118 | def readFieldEnd(self): 119 | pass 120 | 121 | def readMapBegin(self): 122 | pass 123 | 124 | def readMapEnd(self): 125 | pass 126 | 127 | def readListBegin(self): 128 | pass 129 | 130 | def readListEnd(self): 131 | pass 132 | 133 | def readSetBegin(self): 134 | pass 135 | 136 | def readSetEnd(self): 137 | pass 138 | 139 | def readBool(self): 140 | pass 141 | 142 | def readByte(self): 143 | pass 144 | 145 | def readI16(self): 146 | pass 147 | 148 | def readI32(self): 149 | pass 150 | 151 | def readI64(self): 152 | pass 153 | 154 | def readDouble(self): 155 | pass 156 | 157 | def readString(self): 158 | pass 159 | 160 | def skip(self, type): 161 | if type == TType.STOP: 162 | return 163 | elif type == TType.BOOL: 164 | self.readBool() 165 | elif type == TType.BYTE: 166 | self.readByte() 167 | elif type == TType.I16: 168 | self.readI16() 169 | elif type == TType.I32: 170 | self.readI32() 171 | elif type == TType.I64: 172 | self.readI64() 173 | elif type == TType.DOUBLE: 174 | self.readDouble() 175 | elif type == TType.STRING: 176 | self.readString() 177 | elif type == TType.STRUCT: 178 | name = self.readStructBegin() 179 | while True: 180 | (name, type, id) = self.readFieldBegin() 181 | if type == TType.STOP: 182 | break 183 | self.skip(type) 184 | self.readFieldEnd() 185 | self.readStructEnd() 186 | elif type == TType.MAP: 187 | (ktype, vtype, size) = self.readMapBegin() 188 | for i in range(size): 189 | self.skip(ktype) 190 | self.skip(vtype) 191 | self.readMapEnd() 192 | elif type == TType.SET: 193 | (etype, size) = self.readSetBegin() 194 | for i in range(size): 195 | self.skip(etype) 196 | self.readSetEnd() 197 | elif type == TType.LIST: 198 | (etype, size) = self.readListBegin() 199 | for i in range(size): 200 | self.skip(etype) 201 | self.readListEnd() 202 | 203 | # tuple of: ( 'reader method' name, is_container boolean, 'writer_method' name ) 204 | _TTYPE_HANDLERS = ( 205 | (None, None, False), # 0 == TType,STOP 206 | (None, None, False), # 1 == TType.VOID # TODO: handle void? 207 | ('readBool', 'writeBool', False), # 2 == TType.BOOL 208 | ('readByte', 'writeByte', False), # 3 == TType.BYTE and I08 209 | ('readDouble', 'writeDouble', False), # 4 == TType.DOUBLE 210 | (None, None, False), # 5, undefined 211 | ('readI16', 'writeI16', False), # 6 == TType.I16 212 | (None, None, False), # 7, undefined 213 | ('readI32', 'writeI32', False), # 8 == TType.I32 214 | (None, None, False), # 9, undefined 215 | ('readI64', 'writeI64', False), # 10 == TType.I64 216 | ('readString', 'writeString', False), # 11 == TType.STRING and UTF7 217 | ('readContainerStruct', 'writeContainerStruct', True), # 12 == TType.STRUCT 218 | ('readContainerMap', 'writeContainerMap', True), # 13 == TType.MAP 219 | ('readContainerSet', 'writeContainerSet', True), # 14 == TType.SET 220 | ('readContainerList', 'writeContainerList', True), # 15 == TType.LIST 221 | (None, None, False), # 16 == TType.UTF8 # TODO: handle utf8 types? 222 | (None, None, False)# 17 == TType.UTF16 # TODO: handle utf16 types? 223 | ) 224 | 225 | def readFieldByTType(self, ttype, spec): 226 | try: 227 | (r_handler, w_handler, is_container) = self._TTYPE_HANDLERS[ttype] 228 | except IndexError: 229 | raise TProtocolException(type=TProtocolException.INVALID_DATA, 230 | message='Invalid field type %d' % (ttype)) 231 | if r_handler is None: 232 | raise TProtocolException(type=TProtocolException.INVALID_DATA, 233 | message='Invalid field type %d' % (ttype)) 234 | reader = getattr(self, r_handler) 235 | if not is_container: 236 | return reader() 237 | return reader(spec) 238 | 239 | def readContainerList(self, spec): 240 | results = [] 241 | ttype, tspec = spec[0], spec[1] 242 | r_handler = self._TTYPE_HANDLERS[ttype][0] 243 | reader = getattr(self, r_handler) 244 | (list_type, list_len) = self.readListBegin() 245 | if tspec is None: 246 | # list values are simple types 247 | for idx in xrange(list_len): 248 | results.append(reader()) 249 | else: 250 | # this is like an inlined readFieldByTType 251 | container_reader = self._TTYPE_HANDLERS[list_type][0] 252 | val_reader = getattr(self, container_reader) 253 | for idx in xrange(list_len): 254 | val = val_reader(tspec) 255 | results.append(val) 256 | self.readListEnd() 257 | return results 258 | 259 | def readContainerSet(self, spec): 260 | results = set() 261 | ttype, tspec = spec[0], spec[1] 262 | r_handler = self._TTYPE_HANDLERS[ttype][0] 263 | reader = getattr(self, r_handler) 264 | (set_type, set_len) = self.readSetBegin() 265 | if tspec is None: 266 | # set members are simple types 267 | for idx in xrange(set_len): 268 | results.add(reader()) 269 | else: 270 | container_reader = self._TTYPE_HANDLERS[set_type][0] 271 | val_reader = getattr(self, container_reader) 272 | for idx in xrange(set_len): 273 | results.add(val_reader(tspec)) 274 | self.readSetEnd() 275 | return results 276 | 277 | def readContainerStruct(self, spec): 278 | (obj_class, obj_spec) = spec 279 | obj = obj_class() 280 | obj.read(self) 281 | return obj 282 | 283 | def readContainerMap(self, spec): 284 | results = dict() 285 | key_ttype, key_spec = spec[0], spec[1] 286 | val_ttype, val_spec = spec[2], spec[3] 287 | (map_ktype, map_vtype, map_len) = self.readMapBegin() 288 | # TODO: compare types we just decoded with thrift_spec and abort/skip if types disagree 289 | key_reader = getattr(self, self._TTYPE_HANDLERS[key_ttype][0]) 290 | val_reader = getattr(self, self._TTYPE_HANDLERS[val_ttype][0]) 291 | # list values are simple types 292 | for idx in xrange(map_len): 293 | if key_spec is None: 294 | k_val = key_reader() 295 | else: 296 | k_val = self.readFieldByTType(key_ttype, key_spec) 297 | if val_spec is None: 298 | v_val = val_reader() 299 | else: 300 | v_val = self.readFieldByTType(val_ttype, val_spec) 301 | # this raises a TypeError with unhashable keys types. i.e. d=dict(); d[[0,1]] = 2 fails 302 | results[k_val] = v_val 303 | self.readMapEnd() 304 | return results 305 | 306 | def readStruct(self, obj, thrift_spec): 307 | self.readStructBegin() 308 | while True: 309 | (fname, ftype, fid) = self.readFieldBegin() 310 | if ftype == TType.STOP: 311 | break 312 | try: 313 | field = thrift_spec[fid] 314 | except IndexError: 315 | self.skip(ftype) 316 | else: 317 | if field is not None and ftype == field[1]: 318 | fname = field[2] 319 | fspec = field[3] 320 | val = self.readFieldByTType(ftype, fspec) 321 | setattr(obj, fname, val) 322 | else: 323 | self.skip(ftype) 324 | self.readFieldEnd() 325 | self.readStructEnd() 326 | 327 | def writeContainerStruct(self, val, spec): 328 | val.write(self) 329 | 330 | def writeContainerList(self, val, spec): 331 | self.writeListBegin(spec[0], len(val)) 332 | r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]] 333 | e_writer = getattr(self, w_handler) 334 | if not is_container: 335 | for elem in val: 336 | e_writer(elem) 337 | else: 338 | for elem in val: 339 | e_writer(elem, spec[1]) 340 | self.writeListEnd() 341 | 342 | def writeContainerSet(self, val, spec): 343 | self.writeSetBegin(spec[0], len(val)) 344 | r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]] 345 | e_writer = getattr(self, w_handler) 346 | if not is_container: 347 | for elem in val: 348 | e_writer(elem) 349 | else: 350 | for elem in val: 351 | e_writer(elem, spec[1]) 352 | self.writeSetEnd() 353 | 354 | def writeContainerMap(self, val, spec): 355 | k_type = spec[0] 356 | v_type = spec[2] 357 | ignore, ktype_name, k_is_container = self._TTYPE_HANDLERS[k_type] 358 | ignore, vtype_name, v_is_container = self._TTYPE_HANDLERS[v_type] 359 | k_writer = getattr(self, ktype_name) 360 | v_writer = getattr(self, vtype_name) 361 | self.writeMapBegin(k_type, v_type, len(val)) 362 | for m_key, m_val in val.iteritems(): 363 | if not k_is_container: 364 | k_writer(m_key) 365 | else: 366 | k_writer(m_key, spec[1]) 367 | if not v_is_container: 368 | v_writer(m_val) 369 | else: 370 | v_writer(m_val, spec[3]) 371 | self.writeMapEnd() 372 | 373 | def writeStruct(self, obj, thrift_spec): 374 | self.writeStructBegin(obj.__class__.__name__) 375 | for field in thrift_spec: 376 | if field is None: 377 | continue 378 | fname = field[2] 379 | val = getattr(obj, fname) 380 | if val is None: 381 | # skip writing out unset fields 382 | continue 383 | fid = field[0] 384 | ftype = field[1] 385 | fspec = field[3] 386 | # get the writer method for this value 387 | self.writeFieldBegin(fname, ftype, fid) 388 | self.writeFieldByTType(ftype, val, fspec) 389 | self.writeFieldEnd() 390 | self.writeFieldStop() 391 | self.writeStructEnd() 392 | 393 | def writeFieldByTType(self, ttype, val, spec): 394 | r_handler, w_handler, is_container = self._TTYPE_HANDLERS[ttype] 395 | writer = getattr(self, w_handler) 396 | if is_container: 397 | writer(val, spec) 398 | else: 399 | writer(val) 400 | 401 | class TProtocolFactory: 402 | def getProtocol(self, trans): 403 | pass 404 | 405 | -------------------------------------------------------------------------------- /lib/thrift/protocol/TCompactProtocol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from TProtocol import * 21 | from struct import pack, unpack 22 | 23 | __all__ = ['TCompactProtocol', 'TCompactProtocolFactory'] 24 | 25 | CLEAR = 0 26 | FIELD_WRITE = 1 27 | VALUE_WRITE = 2 28 | CONTAINER_WRITE = 3 29 | BOOL_WRITE = 4 30 | FIELD_READ = 5 31 | CONTAINER_READ = 6 32 | VALUE_READ = 7 33 | BOOL_READ = 8 34 | 35 | def make_helper(v_from, container): 36 | def helper(func): 37 | def nested(self, *args, **kwargs): 38 | assert self.state in (v_from, container), (self.state, v_from, container) 39 | return func(self, *args, **kwargs) 40 | return nested 41 | return helper 42 | writer = make_helper(VALUE_WRITE, CONTAINER_WRITE) 43 | reader = make_helper(VALUE_READ, CONTAINER_READ) 44 | 45 | def makeZigZag(n, bits): 46 | return (n << 1) ^ (n >> (bits - 1)) 47 | 48 | def fromZigZag(n): 49 | return (n >> 1) ^ -(n & 1) 50 | 51 | def writeVarint(trans, n): 52 | out = [] 53 | while True: 54 | if n & ~0x7f == 0: 55 | out.append(n) 56 | break 57 | else: 58 | out.append((n & 0xff) | 0x80) 59 | n = n >> 7 60 | trans.write(''.join(map(chr, out))) 61 | 62 | def readVarint(trans): 63 | result = 0 64 | shift = 0 65 | while True: 66 | x = trans.readAll(1) 67 | byte = ord(x) 68 | result |= (byte & 0x7f) << shift 69 | if byte >> 7 == 0: 70 | return result 71 | shift += 7 72 | 73 | class CompactType: 74 | STOP = 0x00 75 | TRUE = 0x01 76 | FALSE = 0x02 77 | BYTE = 0x03 78 | I16 = 0x04 79 | I32 = 0x05 80 | I64 = 0x06 81 | DOUBLE = 0x07 82 | BINARY = 0x08 83 | LIST = 0x09 84 | SET = 0x0A 85 | MAP = 0x0B 86 | STRUCT = 0x0C 87 | 88 | CTYPES = {TType.STOP: CompactType.STOP, 89 | TType.BOOL: CompactType.TRUE, # used for collection 90 | TType.BYTE: CompactType.BYTE, 91 | TType.I16: CompactType.I16, 92 | TType.I32: CompactType.I32, 93 | TType.I64: CompactType.I64, 94 | TType.DOUBLE: CompactType.DOUBLE, 95 | TType.STRING: CompactType.BINARY, 96 | TType.STRUCT: CompactType.STRUCT, 97 | TType.LIST: CompactType.LIST, 98 | TType.SET: CompactType.SET, 99 | TType.MAP: CompactType.MAP 100 | } 101 | 102 | TTYPES = {} 103 | for k, v in CTYPES.items(): 104 | TTYPES[v] = k 105 | TTYPES[CompactType.FALSE] = TType.BOOL 106 | del k 107 | del v 108 | 109 | class TCompactProtocol(TProtocolBase): 110 | "Compact implementation of the Thrift protocol driver." 111 | 112 | PROTOCOL_ID = 0x82 113 | VERSION = 1 114 | VERSION_MASK = 0x1f 115 | TYPE_MASK = 0xe0 116 | TYPE_SHIFT_AMOUNT = 5 117 | 118 | def __init__(self, trans): 119 | TProtocolBase.__init__(self, trans) 120 | self.state = CLEAR 121 | self.__last_fid = 0 122 | self.__bool_fid = None 123 | self.__bool_value = None 124 | self.__structs = [] 125 | self.__containers = [] 126 | 127 | def __writeVarint(self, n): 128 | writeVarint(self.trans, n) 129 | 130 | def writeMessageBegin(self, name, type, seqid): 131 | assert self.state == CLEAR 132 | self.__writeUByte(self.PROTOCOL_ID) 133 | self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT)) 134 | self.__writeVarint(seqid) 135 | self.__writeString(name) 136 | self.state = VALUE_WRITE 137 | 138 | def writeMessageEnd(self): 139 | assert self.state == VALUE_WRITE 140 | self.state = CLEAR 141 | 142 | def writeStructBegin(self, name): 143 | assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state 144 | self.__structs.append((self.state, self.__last_fid)) 145 | self.state = FIELD_WRITE 146 | self.__last_fid = 0 147 | 148 | def writeStructEnd(self): 149 | assert self.state == FIELD_WRITE 150 | self.state, self.__last_fid = self.__structs.pop() 151 | 152 | def writeFieldStop(self): 153 | self.__writeByte(0) 154 | 155 | def __writeFieldHeader(self, type, fid): 156 | delta = fid - self.__last_fid 157 | if 0 < delta <= 15: 158 | self.__writeUByte(delta << 4 | type) 159 | else: 160 | self.__writeByte(type) 161 | self.__writeI16(fid) 162 | self.__last_fid = fid 163 | 164 | def writeFieldBegin(self, name, type, fid): 165 | assert self.state == FIELD_WRITE, self.state 166 | if type == TType.BOOL: 167 | self.state = BOOL_WRITE 168 | self.__bool_fid = fid 169 | else: 170 | self.state = VALUE_WRITE 171 | self.__writeFieldHeader(CTYPES[type], fid) 172 | 173 | def writeFieldEnd(self): 174 | assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state 175 | self.state = FIELD_WRITE 176 | 177 | def __writeUByte(self, byte): 178 | self.trans.write(pack('!B', byte)) 179 | 180 | def __writeByte(self, byte): 181 | self.trans.write(pack('!b', byte)) 182 | 183 | def __writeI16(self, i16): 184 | self.__writeVarint(makeZigZag(i16, 16)) 185 | 186 | def __writeSize(self, i32): 187 | self.__writeVarint(i32) 188 | 189 | def writeCollectionBegin(self, etype, size): 190 | assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state 191 | if size <= 14: 192 | self.__writeUByte(size << 4 | CTYPES[etype]) 193 | else: 194 | self.__writeUByte(0xf0 | CTYPES[etype]) 195 | self.__writeSize(size) 196 | self.__containers.append(self.state) 197 | self.state = CONTAINER_WRITE 198 | writeSetBegin = writeCollectionBegin 199 | writeListBegin = writeCollectionBegin 200 | 201 | def writeMapBegin(self, ktype, vtype, size): 202 | assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state 203 | if size == 0: 204 | self.__writeByte(0) 205 | else: 206 | self.__writeSize(size) 207 | self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype]) 208 | self.__containers.append(self.state) 209 | self.state = CONTAINER_WRITE 210 | 211 | def writeCollectionEnd(self): 212 | assert self.state == CONTAINER_WRITE, self.state 213 | self.state = self.__containers.pop() 214 | writeMapEnd = writeCollectionEnd 215 | writeSetEnd = writeCollectionEnd 216 | writeListEnd = writeCollectionEnd 217 | 218 | def writeBool(self, bool): 219 | if self.state == BOOL_WRITE: 220 | if bool: 221 | ctype = CompactType.TRUE 222 | else: 223 | ctype = CompactType.FALSE 224 | self.__writeFieldHeader(ctype, self.__bool_fid) 225 | elif self.state == CONTAINER_WRITE: 226 | if bool: 227 | self.__writeByte(CompactType.TRUE) 228 | else: 229 | self.__writeByte(CompactType.FALSE) 230 | else: 231 | raise AssertionError, "Invalid state in compact protocol" 232 | 233 | writeByte = writer(__writeByte) 234 | writeI16 = writer(__writeI16) 235 | 236 | @writer 237 | def writeI32(self, i32): 238 | self.__writeVarint(makeZigZag(i32, 32)) 239 | 240 | @writer 241 | def writeI64(self, i64): 242 | self.__writeVarint(makeZigZag(i64, 64)) 243 | 244 | @writer 245 | def writeDouble(self, dub): 246 | self.trans.write(pack('!d', dub)) 247 | 248 | def __writeString(self, s): 249 | self.__writeSize(len(s)) 250 | self.trans.write(s) 251 | writeString = writer(__writeString) 252 | 253 | def readFieldBegin(self): 254 | assert self.state == FIELD_READ, self.state 255 | type = self.__readUByte() 256 | if type & 0x0f == TType.STOP: 257 | return (None, 0, 0) 258 | delta = type >> 4 259 | if delta == 0: 260 | fid = self.__readI16() 261 | else: 262 | fid = self.__last_fid + delta 263 | self.__last_fid = fid 264 | type = type & 0x0f 265 | if type == CompactType.TRUE: 266 | self.state = BOOL_READ 267 | self.__bool_value = True 268 | elif type == CompactType.FALSE: 269 | self.state = BOOL_READ 270 | self.__bool_value = False 271 | else: 272 | self.state = VALUE_READ 273 | return (None, self.__getTType(type), fid) 274 | 275 | def readFieldEnd(self): 276 | assert self.state in (VALUE_READ, BOOL_READ), self.state 277 | self.state = FIELD_READ 278 | 279 | def __readUByte(self): 280 | result, = unpack('!B', self.trans.readAll(1)) 281 | return result 282 | 283 | def __readByte(self): 284 | result, = unpack('!b', self.trans.readAll(1)) 285 | return result 286 | 287 | def __readVarint(self): 288 | return readVarint(self.trans) 289 | 290 | def __readZigZag(self): 291 | return fromZigZag(self.__readVarint()) 292 | 293 | def __readSize(self): 294 | result = self.__readVarint() 295 | if result < 0: 296 | raise TException("Length < 0") 297 | return result 298 | 299 | def readMessageBegin(self): 300 | assert self.state == CLEAR 301 | proto_id = self.__readUByte() 302 | if proto_id != self.PROTOCOL_ID: 303 | raise TProtocolException(TProtocolException.BAD_VERSION, 304 | 'Bad protocol id in the message: %d' % proto_id) 305 | ver_type = self.__readUByte() 306 | type = (ver_type & self.TYPE_MASK) >> self.TYPE_SHIFT_AMOUNT 307 | version = ver_type & self.VERSION_MASK 308 | if version != self.VERSION: 309 | raise TProtocolException(TProtocolException.BAD_VERSION, 310 | 'Bad version: %d (expect %d)' % (version, self.VERSION)) 311 | seqid = self.__readVarint() 312 | name = self.__readString() 313 | return (name, type, seqid) 314 | 315 | def readMessageEnd(self): 316 | assert self.state == CLEAR 317 | assert len(self.__structs) == 0 318 | 319 | def readStructBegin(self): 320 | assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state 321 | self.__structs.append((self.state, self.__last_fid)) 322 | self.state = FIELD_READ 323 | self.__last_fid = 0 324 | 325 | def readStructEnd(self): 326 | assert self.state == FIELD_READ 327 | self.state, self.__last_fid = self.__structs.pop() 328 | 329 | def readCollectionBegin(self): 330 | assert self.state in (VALUE_READ, CONTAINER_READ), self.state 331 | size_type = self.__readUByte() 332 | size = size_type >> 4 333 | type = self.__getTType(size_type) 334 | if size == 15: 335 | size = self.__readSize() 336 | self.__containers.append(self.state) 337 | self.state = CONTAINER_READ 338 | return type, size 339 | readSetBegin = readCollectionBegin 340 | readListBegin = readCollectionBegin 341 | 342 | def readMapBegin(self): 343 | assert self.state in (VALUE_READ, CONTAINER_READ), self.state 344 | size = self.__readSize() 345 | types = 0 346 | if size > 0: 347 | types = self.__readUByte() 348 | vtype = self.__getTType(types) 349 | ktype = self.__getTType(types >> 4) 350 | self.__containers.append(self.state) 351 | self.state = CONTAINER_READ 352 | return (ktype, vtype, size) 353 | 354 | def readCollectionEnd(self): 355 | assert self.state == CONTAINER_READ, self.state 356 | self.state = self.__containers.pop() 357 | readSetEnd = readCollectionEnd 358 | readListEnd = readCollectionEnd 359 | readMapEnd = readCollectionEnd 360 | 361 | def readBool(self): 362 | if self.state == BOOL_READ: 363 | return self.__bool_value == CompactType.TRUE 364 | elif self.state == CONTAINER_READ: 365 | return self.__readByte() == CompactType.TRUE 366 | else: 367 | raise AssertionError, "Invalid state in compact protocol: %d" % self.state 368 | 369 | readByte = reader(__readByte) 370 | __readI16 = __readZigZag 371 | readI16 = reader(__readZigZag) 372 | readI32 = reader(__readZigZag) 373 | readI64 = reader(__readZigZag) 374 | 375 | @reader 376 | def readDouble(self): 377 | buff = self.trans.readAll(8) 378 | val, = unpack('!d', buff) 379 | return val 380 | 381 | def __readString(self): 382 | len = self.__readSize() 383 | return self.trans.readAll(len) 384 | readString = reader(__readString) 385 | 386 | def __getTType(self, byte): 387 | return TTYPES[byte & 0x0f] 388 | 389 | 390 | class TCompactProtocolFactory: 391 | def __init__(self): 392 | pass 393 | 394 | def getProtocol(self, trans): 395 | return TCompactProtocol(trans) 396 | -------------------------------------------------------------------------------- /lib/evernote/edam/error/ttypes.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | 11 | from thrift.transport import TTransport 12 | from thrift.protocol import TBinaryProtocol, TProtocol 13 | try: 14 | from thrift.protocol import fastbinary 15 | except: 16 | fastbinary = None 17 | 18 | 19 | class EDAMErrorCode(object): 20 | """ 21 | Numeric codes indicating the type of error that occurred on the 22 | service. 23 |