Anonymous avatar Anonymous committed 4580637

Initial commit

Comments (0)

Files changed (8)

+#!/usr/bin/env python
+
+
+import os
+from distutils.core import setup
+
+packages, data_files = [], []
+root_dir = os.path.dirname(__file__)
+
+
+for dirpath, dirnames, filenames in os.walk('yafotki'):
+    for i, dirname in enumerate(dirnames):
+        if dirname.startswith('.'): del dirnames[i]
+    if '__init__.py' in filenames:
+        pkg = dirpath.replace(os.path.sep, '.')
+        if os.path.altsep:
+            pkg = pkg.replace(os.path.altsep, '.')
+        packages.append(pkg)
+    elif filenames:
+        prefix = dirpath[10:]
+        for f in filenames:
+            data_files.append(os.path.join(prefix, f))
+
+setup(
+    name='django-yafotki',
+    description='Yandex.Fotki files backend for django',
+    author='Serge A Makarov',
+    author_email='serg.makar@gmail.com',
+    license = 'BSD Licence',
+    version = '0.1',
+    url='',
+    package_dir={'yafotki': 'yafotki'},
+    packages=packages,
+    package_data={'yafotki': data_files},
+    requires = ['feedparser']
+)

yafotki/client/client.py

+#-*- coding:utf-8 -*-
+
+import os
+import urllib
+import xml.dom.minidom as minidom
+from xml.etree import ElementTree
+import feedparser
+import yarsa
+import http_client
+
+#TODO: Add exceptions for all errors
+class YFClientException(Exception):
+    pass
+
+class YFCreateObjectException(YFClientException):
+    pass
+
+
+class YFClient(object):
+
+    APP_NAMESPACE = 'http://www.w3.org/2007/app'
+    ATOM_NAMESPACE = 'http://www.w3.org/2005/Atom'
+
+    rsa_url = 'http://auth.mobile.yandex.ru/yamrsa/key/'
+    rsa_key = {}
+    rsa_request_id = None
+    token_url = 'http://auth.mobile.yandex.ru/yamrsa/token/'
+    service_doc_url = None
+    albums_link = None
+    photoes_link = None
+    atom_processor = None
+
+    def __init__(self, username = None, password = None):
+        self.username = username
+        self.password = password
+        self.http_client = http_client.YaAtomHttpClient()
+        if username is not None:
+            self.service_doc_url = 'http://api-fotki.yandex.ru/api/users/%s/' % self.username
+        self.get_service_doc()
+        if self.username and self.password:
+            self.login()
+
+
+    def login(self):
+        self.get_rsa()
+        self.encrypted_login = yarsa.encrypt("<credentials login='%s' password='%s'/>" % (self.username, self.password,), self.rsa_key)
+        login_params = urllib.urlencode({
+            'request_id': self.rsa_request_id,
+            'credentials': self.encrypted_login
+        })
+        login_headers = {
+            'Host': 'auth.mobile.yandex.ru',
+            'Content-Type': 'application/x-www-form-urlencoded'
+        }
+        response_data = self.http_client.post(self.token_url, login_params, login_headers).read()
+        xml = minidom.parseString(response_data)
+        token_node = xml.getElementsByTagName('token')[0]
+        self.token = token_node.firstChild.nodeValue
+        self.http_client.set_token(self.token)
+
+    def get_service_doc(self):
+        service_doc = self.http_client.get(self.service_doc_url).read()
+        service_tree = ElementTree.fromstring(service_doc)
+        service_collections = service_tree.findall('{%s}workspace/{%s}collection' % (self.APP_NAMESPACE, self.APP_NAMESPACE))
+        self.albums_link = service_collections[0].get('href')
+        self.photoes_link = service_collections[1].get('href')
+
+    def get_albums(self):
+        pass
+
+    def add_album(self, title, summary = None):
+        data = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:f="yandex:fotki"><title>%s</title><summary>%s</summary></entry>' % (title, summary,)
+        response = self.http_client.post(self.albums_link, data, {
+            'Content-Type': 'application/atom+xml; charset=utf-8; type=entry'
+        })
+        if response.status == 201:
+            print "Album created"
+            return feedparser.parse(response.read())
+        else:
+            raise YFCreateObjectException("Yandex Says: %s - %s" % (response.status, response.reason,)).__class__, YFCreateObjectException
+
+    def add_photo(self, album, filename, content, content_type):
+        if self.get_album_by_name(album):
+            album_entry = self.get_album_by_name(album)
+        else:
+            album_entry = self.add_album(album)[0]
+        headers = dict()
+        headers['Content-Type'] = content_type
+        headers['Slug'] = os.path.basename(filename)
+        new_photo_response = self.http_client.post(album_entry.links[2].href, content, headers).read()
+        return feedparser.parse(new_photo_response)
+
+    def get_album_by_name(self, album_title):
+        albums_data = self.http_client.get(self.albums_link).read()
+        f = feedparser.parse(albums_data)
+        for entry in f.entries:
+            if entry.title == album_title:
+                return entry
+        return False
+
+    def get_album_photoes(self, album_name):
+        album = self.get_album_by_name(album_name)
+        list_link = album.links[2].href
+        list_xml = self.http_client.get(list_link).read()
+        f = feedparser.parse(list_xml)
+        return f.entries
+
+    def is_exist(self, album, name):
+        #TODO: Create test for photo is exist
+        return False
+
+    def get_rsa(self):
+        rsa_data = self.http_client.get(self.rsa_url).read()
+        xml = minidom.parseString(rsa_data)
+        rsa_key_node = xml.getElementsByTagName('key')[0]
+        self.rsa_key = rsa_key_node.firstChild.nodeValue.encode('ascii')
+        rsa_id_node = xml.getElementsByTagName('request_id')[0]
+        self.rsa_request_id = rsa_id_node.firstChild.nodeValue

yafotki/client/http.py

+#-*- coding:utf-8 -*-
+
+import httplib
+from urlparse import urlparse
+import types
+
+def parse_url(url):
+    url_parts = urlparse(url)
+    url = Url(url_parts)
+    return url
+
+    
+
+class Url(object):
+
+    protocol = None
+    host = None
+    port = None
+    path = None
+
+    def __init__(self, url_parts):
+        if url_parts[0]:
+            self.protocol = url_parts[0]
+        else:
+            self.protocol = 'http'
+
+        if url_parts[1]:
+            host_parts = url_parts[1].split(":")
+            if host_parts[0]:
+                self.host = host_parts[0]
+            try:
+                self.port = int(host_parts[1])
+            except IndexError:
+                self.port = 80
+        if url_parts[2]:
+            self.path = url_parts[2]
+
+
+class YandexHttpClientException(Exception):
+    pass
+
+
+DEFAULT_CONTENT_TYPE = 'application/atom+xml'
+DEFAULT_CHARSET = 'charset=utf-8'
+
+class HttpClient(object):
+    debug = False
+    headers = {}
+
+    def __init__(self, headers = None, debug = None):
+        self.debug = debug or False
+        self.headers = headers or {}
+
+
+    def request(self, method, url, data = None, headers = None):
+        all_headers = self.headers.copy()
+        if headers:
+            all_headers.update(headers)
+        if data and 'Content-Length' not in all_headers:
+            if isinstance(data, types.StringTypes):
+                all_headers['Content-Length'] = str(len(data))
+            else:
+                pass
+
+        if 'Content-Type' not in all_headers:
+            all_headers['Content-Type'] = DEFAULT_CONTENT_TYPE
+        url_object = parse_url(url)
+        if 'Host' not in all_headers:
+            if url_object.host:
+                all_headers['Host'] = url_object.host
+        connection = self.init_connection(url)
+        connection.request(method, url_object.path, data, all_headers)
+        return connection.getresponse()
+
+
+    def init_connection(self, url):
+        url = parse_url(url)
+        return httplib.HTTPConnection(url.host, url.port)
+   

yafotki/client/http_client.py

+#-*- coding:utf-8 -*-
+
+import http
+
+class YaAtomHttpClient(object):
+    http_client = None
+    auth_token = None
+
+    def __init__(self, http_client = None, auth_token = None):
+
+        self.http_client = http_client or http.HttpClient()
+        self.auth_token = auth_token or None
+
+    def set_token(self, token):
+        self.auth_token = token
+
+    def request(self, method = None, uri = None, data = None, headers = None ):
+        request_headers = {}
+        if headers:
+            request_headers.update(headers)
+        if self.auth_token:
+            request_headers['Authorization'] = 'FimpToken realm="fotki.yandex.ru", token="%s"' % self.auth_token
+
+        return self.http_client.request(method, uri, data,request_headers)
+
+
+    def get(self, uri, headers = None):
+        return self.request('GET', uri = uri, headers = headers )
+
+    def post(self, uri, data = None, headers = None):
+        return self.request('POST', uri = uri, data = data,headers = headers )
+
+    def put(self, uri, data = None, headers = None):
+        return self.request('PUT', uri = uri, data = data,headers = headers )
+
+    def delete(self, uri, data = None, headers = None):
+        return self.request('DELETE', uri = uri, data = data,headers = headers )
+
+
+
+
+

yafotki/client/yarsa.py

+#-*- coding:utf-8 -*-
+
+def encrypt(message, public_key):
+    NSTR, ESTR = public_key.split("#")
+    DATA_ARR = [ord(x) for x in message]
+    N,E,STEP_SIZE = int(NSTR,16),int(ESTR,16), len(NSTR)/2-1
+    prev_crypted = [0]*STEP_SIZE
+    hex_out = ""
+    for i in range(0,(len(DATA_ARR)-1)/STEP_SIZE+1):
+        tmp = DATA_ARR[i*STEP_SIZE:(i+1)*STEP_SIZE]
+        tmp = [tmp[i] ^ prev_crypted[i] for i in range(0,len(tmp))]
+        tmp.reverse()
+        plain = 0
+        for x in range(0,len(tmp)): plain+= tmp[x]*pow(256, x, N)
+        hex_result = "%x" % pow(plain,E,N)
+        hex_result = "".join(['0']*( len(NSTR)- len(hex_result))) + hex_result
+
+        for x in range(0,min(len(hex_result),len(prev_crypted)*2),2):
+            prev_crypted[x/2] = int(hex_result[x:x+2],16)
+
+        hex_out += ("0" if len(tmp) < 16 else "") + ("%x" % len(tmp)) + "00" # current size
+        ks = len(NSTR)/2
+        hex_out += ("0" if ks < 16 else "") + ("%x" % ks) + "00" # key size
+        hex_out += hex_result
+
+    return hex_out.decode("hex").encode("base64").replace("\n","")
+    

yafotki/fields.py

+# -*- coding: UTF-8 -*-
+
+from django.db.models.fields.files import  FieldFile,FileField
+from django_yafotki.yafotki.storage import YFStorage
+
+class YFFieldFile(FieldFile):
+
+    def __init__(self, *args, **kwargs):
+        super(YFFieldFile, self).__init__(*args, **kwargs)
+        self.storage = YFStorage()
+
+
+class YFField(FileField):
+    attr_class = YFFieldFile
+
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('upload_to', 'default')
+        kwargs.setdefault('max_length', 255)
+        super(YFField, self).__init__(*args, **kwargs)

yafotki/models.py

+from django.db import models
+
+# Create your models here.

yafotki/storage.py

+# -*- coding: UTF-8 -*-
+
+import os
+import imghdr
+
+from django.conf import settings
+from django.core.files.storage import Storage
+from django_yafotki.yafotki.client.client import YFClient
+
+
+#TODO: Make queries to YF(e.g Authorization in YF) only in 
+class YFStorage(Storage):
+
+    options = None
+    yf_client = None
+
+    def __init__(self, options = None):
+        self.options = options or settings.YAFOTKI_STORAGE_OPTIONS
+        self.yf_client = YFClient(self.options['username'], self.options['password'])
+
+
+    def _open(self, name, mode = 'rb'):
+        pass
+
+    def _save(self, name, content):
+        album_name, image_name = os.path.split(name)
+        if not album_name:
+            #TODO: Get default album from config
+            album_name = 'default'
+        content.seek(0)
+        content_type = 'image/%s' % (imghdr.what(image_name, content.read(2048)))
+        content.seek(0)
+        photo_obj = self.yf_client.add_photo(album_name, image_name, content.file, content_type)
+        return photo_obj.entries[0].links[3].href
+
+
+    def delete(self, name):
+        pass
+
+    def exists(self, name):
+        album_name, file_name = os.path.split(name)
+        if not album_name:
+            album_name = 'default'
+        return self.yf_client.is_exist(album_name, file_name)
+
+
+    def listdir(self, path):
+        pass
+
+    def size(self, name):
+        print name
+
+    def url(self, path):
+        return path
+
+
+    
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.