1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00
This commit is contained in:
max 2012-04-29 16:47:40 +07:00
commit f9ccca120f
9 changed files with 581 additions and 81 deletions

433
libs/privilege.py Normal file
View File

@ -0,0 +1,433 @@
#! /usr/bin/python
"""
privilege.py
Copyright (c) 2009-2010, Travis H.
License terms: Same as those of Python itself.
This module is designed to implement the privilege-dropping API
described here:
http://www.cs.berkeley.edu/~daw/papers/setuid-usenix02.pdf
http://www.cs.berkeley.edu/~daw/papers/setuid-login08b.pdf
This topic is complex so you should read at least the second paper
before trying to understand why I'm doing this.
NOTE: This code does not (yet) attempt to work on Solaris and AIX.
That is, you must have getres[ug]id and setres[ug]id to use it.
TODO:
I have added setres[ug]id to python; unsure of when it will
appear; make a future version of this will check for its presence.
Make interface more OO, less imperative.
Implement drop_priv_temp, restore_priv.
Implement on systems without setres[ug]id
Contributors:
Kevin Gilette
"""
from ctypes import *
from ctypes.util import find_library
import os
import pwd
import grp
# Get the name of the Operating System
os_kernel = os.uname()[0]
class PrivilegeFail(Exception):
pass
clib = CDLL(find_library("c"))
# Tested on x86-64 -- all tests pass when run as root
__uid_t = c_uint
__gid_t = c_uint
_getresuid = clib.getresuid
def getresuid():
"""Get the real, effective, and saved user IDs"""
# Create some memory locations for getresuid to write to.
r = __uid_t()
e = __uid_t()
s = __uid_t()
# Call getresuid syscall, passing by reference.
res = _getresuid(byref(r), byref(e), byref(s))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
# Convert to python integers.
return r.value, e.value, s.value
_getresgid = clib.getresgid
def getresgid():
"""Get the real, effective, and saved group IDs"""
r = __gid_t()
e = __gid_t()
s = __gid_t()
# Call getresgid syscall, passing by reference.
res = _getresgid(byref(r), byref(e), byref(s))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
# Convert to python integers.
return r.value, e.value, s.value
# Import the setresuid system call using ctypes
_setresuid = clib.setresuid
_setresuid.argttypes = [__uid_t, __uid_t, __uid_t]
_setresuid.resttype = c_int
# Import the setresgid system call using ctypes
_setresgid = clib.setresgid
_setresgid.argttypes = [__gid_t, __gid_t, __gid_t]
_setresgid.resttype = c_int
def setresuid(ruid, euid, suid):
"""Set the real, effective, and saved user IDs"""
res = _setresuid(__uid_t(ruid), __uid_t(euid), __uid_t(suid))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
def setresgid(rgid, egid, sgid):
"""Set the real, effecive, and saved group IDs"""
res = _setresgid(__gid_t(rgid), __gid_t(egid), __gid_t(sgid))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
def sort_uniq(args):
"""Sort a sequence, discarding duplicates."""
return sorted(set(args))
class user_credentials:
"""
This represents the credentials associated with a user.
User ID
Group ID
Supplementary Groups
This is used as an argument to drop_permanently
"""
def __init__(self, uid, gid, sups):
# -1 has special meaning for several set*id calls (ignore)
if uid == -1: raise PrivilegeFail
if gid == -1: raise PrivilegeFail
nm = os.sysconf('SC_NGROUPS_MAX')
if nm < 0 or nm < len(sups): raise PrivilegeFail
self.uid = uid
self.gid = gid
self.sups = sort_uniq(sups)
def eql_sups(current, target):
"""
Compare two supplementary group lists, and ignore if effective GID is in current but not target.
Prerequisite: The supplementary group lists are sorted and filtered for duplicates.
"""
egid = os.getegid()
my_current = sort_uniq(current)
# Instead of tediously ignoring this value, if it's in the current list, then go ahead
# and add it to the target list
if egid in current:
my_target = target + [ egid ]
else:
my_target = target
my_target = sort_uniq(my_target)
return my_current == my_target
def get_sups():
"""This is here to give us a layer of abstraction relative to system calls"""
return os.getgroups()
def set_sups(target_sups):
"""
This is designed to give us a layer of abstraction from the system calls.
It also accomodates FreeBSD's idiosyncracy (which is POSIX-compliant) of
keeping the egid in the supplementary groups list.
It also makes an effort to not call the setgroups routine if the target
group list is identical to the current one in force.
"""
global os_kernel
if os_kernel == 'FreeBSD':
target_sups = [ os.getegid() ] + target_sups
if os.geteuid() == 0:
# This will raise an OSError exception if it fails
os.setgroups(target_sups)
else:
cur_sups = get_sups()
# This will probably fail
if not eql_sups(cur_sups, target_sups):
# This will raise an OSError exception if it fails
os.setgroups(target_sups)
return True
def set_gids(r, e, s):
"""This is here to give us a layer of abstraction relative to system calls"""
setresgid(r, e, s)
def set_uids(r, e, s):
"""This is here to give us a layer of abstraction relative to system calls"""
setresuid(r, e, s)
class res_ids:
"""
This represents the three IDs (group or user) associated with a process.
"""
def __init__(self, real, effective, saved):
self.r = real
self.e = effective
self.s = saved
class proc_credentials:
"""
This obtains and represents the credentials associated with a process.
"""
def __init__(self):
self.uids = apply(res_ids, getresuid())
self.gids = apply(res_ids, getresgid())
self.sups = sort_uniq(os.getgroups())
def get_fs_ids():
"""Get filesystem IDs - applies only to Linux"""
uid = None
gid = None
file = open('/proc/self/status', 'r')
for line in file:
fields = line.split()
if fields[0] == 'Uid:':
uid = int(fields[4])
elif fields[0] == 'Gid:':
gid = int(fields[4])
return uid, gid
def coerce_user(user):
if hasattr(user, '__int__'):
return int(user)
return pwd.getpwnam(user).pw_uid
def coerce_group(group):
if hasattr(group, '__int__'):
return int(group)
return grp.getgrnam(group).gr_gid
def drop_privileges_permanently(uid, gid, sups):
"""
This routine is designed to permanently drop all privileges to the
user, group, and supplementary groups specified.
"""
uid = coerce_user(uid)
gid = coerce_group(gid)
sups = map(coerce_group, sups)
# This does some syntax checking
ucred = user_credentials(uid, gid, sups)
# This is for our convenience
u = uid
g = gid
# Order is important in these three calls
set_sups(ucred.sups)
set_gids(g, g, g) # real, effective, saved
set_uids(u, u, u) # real, effective, saved
# Check that we actually did what we expected or throw exception.
pc = proc_credentials()
# Portably compare the supplementary group list
if not eql_sups(pc.sups, ucred.sups): raise PrivilegeFail
# Check all the gids
if not (g == pc.gids.r and g == pc.gids.e and g == pc.gids.s):
raise PrivilegeFail
# Check all the uids
if not (u == pc.uids.r and u == pc.gids.e and u == pc.uids.s):
raise PrivilegeFail
global os_kernel
if os_kernel == 'Linux':
if get_fs_ids() != (u, g): raise PrivilegeFail
# This is all test code
# It is run if this script is invoked directly
if __name__ == '__main__':
import unittest
class test_getresXid(unittest.TestCase):
"""Test the calls to getresXid"""
def test__getresuid(self):
# TODO: is there any way to avoid redefining this here? I tried a global but it didn't
# work.
# Note: the double-leading underscore may be getting special-cased by python.
__uid_t = c_int
r = __uid_t()
e = __uid_t()
s = __uid_t()
ret = _getresuid(byref(r), byref(e), byref(s))
self.assertEqual(ret, 0)
self.assertEqual(r.value, os.getuid())
self.assertEqual(e.value, os.geteuid())
# NOTE: no other portable way to get saved UID
def test_getresuid(self):
(r, e, s) = getresuid()
self.assertEqual(r, os.getuid())
self.assertEqual(e, os.geteuid())
# NOTE: no other portable way to get saved UID
def test__getresgid(self):
__gid_t = c_int
r = __gid_t()
e = __gid_t()
s = __gid_t()
ret = _getresgid(byref(r), byref(e), byref(s))
self.assertEqual(ret, 0)
self.assertEqual(r.value, os.getgid())
self.assertEqual(e.value, os.getegid())
# NOTE: no other portable way to get saved GID
def test_getresgid(self):
(r, e, s) = getresgid()
self.assertEqual(r, os.getgid())
self.assertEqual(e, os.getegid())
# NOTE: no other portable way to get saved GID
class test_setresuid(unittest.TestCase):
"""Test the call to setresuid"""
def setUp(self):
self.uid = os.geteuid()
def test__setresuid(self):
__uid_t = c_int
r1 = __uid_t(1)
e1 = __uid_t(1)
# Must save root UID so that we can reset UIDs for other tests
s1 = __uid_t(0)
if self.uid == 0:
rv = _setresuid(r1, e1, s1)
self.assertEqual(rv, 0)
(r2, e2, s2) = getresuid()
self.assertEqual(r1.value, r2)
self.assertEqual(e1.value, e2)
self.assertEqual(s1.value, s2)
else:
rv = _setresuid(r1, e1, s1)
self.assertEqual(rv, -1)
def test_setresuid(self):
if self.uid == 0:
setresuid(1,1,0)
(r, e, s) = getresuid()
self.assertEqual(r, 1)
self.assertEqual(e, 1)
self.assertEqual(s, 0)
else:
self.assertRaises(OSError, setresuid, 1, 1, 1)
def tearDown(self):
if self.uid == 0:
# Restore UIDs for next test
_setresuid(0, 0, 0)
class test_setresgid(unittest.TestCase):
"""Test the call to setresgid"""
def setUp(self):
self.uid = os.geteuid()
def test__setresgid(self):
__gid_t = c_int
r1 = __gid_t(1)
e1 = __gid_t(1)
s1 = __gid_t(1)
if self.uid == 0:
rv = _setresgid(r1, e1, s1)
self.assertEqual(rv, 0)
(r2, e2, s2) = getresgid()
self.assertEqual(r1.value, r2)
self.assertEqual(e1.value, e2)
self.assertEqual(s1.value, s2)
else:
rv = _setresgid(r1, e1, s1)
self.assertEqual(rv, -1)
def test_setresgid(self):
if self.uid == 0:
setresgid(1,1,1)
(r, e, s) = getresgid()
self.assertEqual(r, 1)
self.assertEqual(e, 1)
self.assertEqual(s, 1)
else:
self.assertRaises(OSError, setresgid, 1, 1, 1)
class test_sort_uniq(unittest.TestCase):
def test_sort_uniq(self):
l = [ 'c', 'a', 'b', 'a' ]
self.assertEqual(sort_uniq(l), [ 'a', 'b', 'c'])
class test_user_credentials(unittest.TestCase):
def test_negatives(self):
self.assertRaises(PrivilegeFail, user_credentials, -1, 0, [])
self.assertRaises(PrivilegeFail, user_credentials, 0, -1, [])
uc = user_credentials(0, 0, [0, 1])
class test_eql_sups(unittest.TestCase):
def test_equal(self):
self.assert_(eql_sups([0, 1, 2], [0, 1, 2]))
def test_contains_egid(self):
self.assert_(eql_sups(sort_uniq([0, 1, 2, os.getegid()]), [0, 1, 2]))
self.assert_(eql_sups(sort_uniq([0, 1, os.getegid(), 9999]), [0, 1, 9999]))
class test_get_sups(unittest.TestCase):
def test(self):
self.assertEqual(os.getgroups(), get_sups())
class test_set_sups(unittest.TestCase):
def test_equal(self):
sups = os.getgroups()
set_sups(sups)
self.assertEqual(sups, os.getgroups())
def test_unequal(self):
old_sups = os.getgroups()
sups = [ 1, 2, 3 ]
if os.geteuid() == 0:
set_sups(sups)
self.assert_(eql_sups(os.getgroups(), sups))
# Clean up by resetting supplementary groups
set_sups(old_sups)
else:
self.assertRaises(OSError, set_sups, sups)
class test_res_ids(unittest.TestCase):
def test_res_ids(self):
ids = res_ids(1, 2, 3)
self.assertEqual(ids.r, 1)
self.assertEqual(ids.e, 2)
self.assertEqual(ids.s, 3)
class test_proc_credentials(unittest.TestCase):
def test_pc(self):
pc = proc_credentials()
self.assertEqual(pc.uids.r, os.getuid())
self.assertEqual(pc.uids.e, os.geteuid())
self.assertEqual(pc.uids.s, (getresuid())[2])
self.assertEqual(pc.gids.r, os.getgid())
self.assertEqual(pc.gids.e, os.getegid())
self.assertEqual(pc.gids.s, (getresgid())[2])
self.assertEqual(pc.sups, sort_uniq(os.getgroups()))
class test_get_fs_ids(unittest.TestCase):
def test_get_fs_ids(self):
uid, gid = get_fs_ids()
self.assertEqual(uid, os.getuid())
self.assertEqual(gid, os.getgid())
class test_drop_privs(unittest.TestCase):
def test_drop_privs(self):
"""This test must be run last"""
if os.geteuid() == 0:
drop_privileges_permanently(1, 1, [1])
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(test_getresXid))
suite.addTest(unittest.makeSuite(test_setresuid))
suite.addTest(unittest.makeSuite(test_setresgid))
suite.addTest(unittest.makeSuite(test_sort_uniq))
suite.addTest(unittest.makeSuite(test_user_credentials))
suite.addTest(unittest.makeSuite(test_eql_sups))
suite.addTest(unittest.makeSuite(test_set_sups))
suite.addTest(unittest.makeSuite(test_res_ids))
suite.addTest(unittest.makeSuite(test_proc_credentials))
suite.addTest(unittest.makeSuite(test_get_fs_ids))
suite.addTest(unittest.makeSuite(test_drop_privs))
unittest.TextTestRunner().run(suite)

View File

@ -15,4 +15,14 @@ DEBUG = True
# absolute path where the paste files should be store
# default in projectdirectory/static/content/
# use "/" even under Windows
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
# Port and host the embeded python server should be using in prod and in dev
PROD_HOST = "0.0.0.0"
PROD_PORT= "80"
DEV_HOST = "127.0.0.1"
DEV_PORT= "8000"
# User and group the server should run as. Set to None if it should be the
# current user
USER = None
GROUP = None

View File

@ -153,7 +153,7 @@ class Paste(object):
if self.expiration == "burn_after_reading":
self.expiration = self.expiration + '#%s' % datetime.now()
# writethe paste
# write the paste
with open(self.path, 'w') as f:
f.write(unicode(self.expiration) + '\n')
f.write(self.content + '\n')

View File

@ -7,6 +7,10 @@
import os
import hashlib
import thread
import time
import tempfile
import glob
from datetime import datetime, timedelta
@ -14,6 +18,8 @@ from src import settings, setup_path, Paste
setup_path()
from privilege import drop_privileges_permanently, coerce_user, coerce_group
from bottle import (Bottle, route, run, abort,
static_file, debug, view, request)
@ -86,8 +92,32 @@ def server_static(filename):
if __name__ == "__main__":
def drop_privileges():
time.sleep(5)
if settings.USER:
settings.GROUP = settings.GROUP or settings.USER
try:
user = coerce_user(settings.USER)
group = coerce_group(settings.GROUP)
lock_files = glob.glob(os.path.join(tempfile.gettempdir(),
'bottle.*.lock'))
for lock_file in lock_files:
os.chown(lock_file, user, group)
drop_privileges_permanently(settings.USER, settings.GROUP, ())
except Exception:
print "Failed to drop privileges. Running with current user."
thread.start_new_thread(drop_privileges, ())
if settings.DEBUG:
debug(True)
run(app, host='localhost', port=8000, reloader=True, server="cherrypy")
run(app, host=settings.DEV_HOST, port=settings.DEV_PORT,
reloader=True, server="cherrypy")
else:
run(app, host='localhost', port=8000, server="cherrypy")
run(app, host=settings.PROD_HOST,
port=settings.PROD_PORT, server="cherrypy")

View File

@ -41,7 +41,7 @@ body {
.paste-option {
float:right;
}
select {
width: 135px;
@ -152,17 +152,16 @@ ol.linenums span:first-child {
li.L0, li.L1, li.L2, li.L3, li.L4,
li.L5, li.L6, li.L7, li.L8, li.L9
{
list-style-type: decimal;
background: inherit;
list-style-type: decimal;
background: inherit;
}
#clip-container div:hover,
#clip-button:hover,
#clip-button a:hover{
a#clip-button.hover{
cursor:pointer;
text-decoration:underline;
}
#copy-success {
#copy-success, #short-url-success {
display:none;
}

View File

@ -2,12 +2,12 @@
// Author: Joseph Huckaby
var ZeroClipboard = {
version: "1.0.7",
clients: {}, // registered upload clients on page, indexed by id
moviePath: 'ZeroClipboard.swf', // URL to movie
nextId: 1, // ID of next movie
$: function(thingy) {
// simple DOM lookup utility function
if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
@ -34,31 +34,31 @@ var ZeroClipboard = {
}
return thingy;
},
setMoviePath: function(path) {
// set path to ZeroClipboard.swf
this.moviePath = path;
},
dispatch: function(id, eventName, args) {
// receive event from flash movie, send to client
// receive event from flash movie, send to client
var client = this.clients[id];
if (client) {
client.receiveEvent(eventName, args);
}
},
register: function(id, client) {
// register new client to receive events
this.clients[id] = client;
},
getDOMObjectPosition: function(obj, stopObj) {
// get absolute coordinates for dom element
var info = {
left: 0,
top: 0,
width: obj.width ? obj.width : obj.offsetWidth,
left: 0,
top: 0,
width: obj.width ? obj.width : obj.offsetWidth,
height: obj.height ? obj.height : obj.offsetHeight
};
@ -70,25 +70,25 @@ var ZeroClipboard = {
return info;
},
Client: function(elem) {
// constructor for new simple upload client
this.handlers = {};
// unique ID
this.id = ZeroClipboard.nextId++;
this.movieId = 'ZeroClipboardMovie_' + this.id;
// register client with singleton to receive flash events
ZeroClipboard.register(this.id, this);
// create movie
if (elem) this.glue(elem);
}
};
ZeroClipboard.Client.prototype = {
id: 0, // unique ID for us
ready: false, // whether movie is ready to receive events or not
movie: null, // reference to movie object
@ -96,28 +96,28 @@ ZeroClipboard.Client.prototype = {
handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
cssEffects: true, // enable CSS mouse effects on dom container
handlers: null, // user event handlers
glue: function(elem, appendElem, stylesToAdd) {
// glue to DOM element
// elem can be ID or actual DOM element object
this.domElement = ZeroClipboard.$(elem);
// float just above object, or zIndex 99 if dom element isn't set
var zIndex = 99;
if (this.domElement.style.zIndex) {
zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
}
if (typeof(appendElem) == 'string') {
appendElem = ZeroClipboard.$(appendElem);
}
else if (typeof(appendElem) == 'undefined') {
appendElem = document.getElementsByTagName('body')[0];
}
// find X/Y position of domElement
var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem);
// create floating DIV above element
this.div = document.createElement('div');
var style = this.div.style;
@ -127,27 +127,27 @@ ZeroClipboard.Client.prototype = {
style.width = '' + box.width + 'px';
style.height = '' + box.height + 'px';
style.zIndex = zIndex;
if (typeof(stylesToAdd) == 'object') {
for (addedStyle in stylesToAdd) {
style[addedStyle] = stylesToAdd[addedStyle];
}
}
// style.backgroundColor = '#f00'; // debug
//style.backgroundColor = '#f00'; // debug
appendElem.appendChild(this.div);
this.div.innerHTML = this.getHTML( box.width, box.height );
},
getHTML: function(width, height) {
// return HTML for movie
var html = '';
var flashvars = 'id=' + this.id +
'&width=' + width +
var flashvars = 'id=' + this.id +
'&width=' + width +
'&height=' + height;
if (navigator.userAgent.match(/MSIE/)) {
// IE gets an OBJECT tag
var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
@ -159,33 +159,33 @@ ZeroClipboard.Client.prototype = {
}
return html;
},
hide: function() {
// temporarily hide floater offscreen
if (this.div) {
this.div.style.left = '-2000px';
}
},
show: function() {
// show ourselves after a call to hide()
this.reposition();
},
destroy: function() {
// destroy control and floater
if (this.domElement && this.div) {
this.hide();
this.div.innerHTML = '';
var body = document.getElementsByTagName('body')[0];
try { body.removeChild( this.div ); } catch(e) {;}
this.domElement = null;
this.div = null;
}
},
reposition: function(elem) {
// reposition our floating div, optionally to new container
// warning: container CANNOT change size, only position
@ -193,7 +193,7 @@ ZeroClipboard.Client.prototype = {
this.domElement = ZeroClipboard.$(elem);
if (!this.domElement) this.hide();
}
if (this.domElement && this.div) {
var box = ZeroClipboard.getDOMObjectPosition(this.domElement);
var style = this.div.style;
@ -201,13 +201,13 @@ ZeroClipboard.Client.prototype = {
style.top = '' + box.top + 'px';
}
},
setText: function(newText) {
// set text to be copied to clipboard
this.clipText = newText;
if (this.ready) this.movie.setText(newText);
},
addEventListener: function(eventName, func) {
// add user event listener for event
// event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
@ -215,22 +215,22 @@ ZeroClipboard.Client.prototype = {
if (!this.handlers[eventName]) this.handlers[eventName] = [];
this.handlers[eventName].push(func);
},
setHandCursor: function(enabled) {
// enable hand cursor (true), or default arrow cursor (false)
this.handCursorEnabled = enabled;
if (this.ready) this.movie.setHandCursor(enabled);
},
setCSSEffects: function(enabled) {
// enable or disable CSS effects on DOM container
this.cssEffects = !!enabled;
},
receiveEvent: function(eventName, args) {
// receive event from flash
eventName = eventName.toString().toLowerCase().replace(/^on/, '');
// special behavior for certain events
switch (eventName) {
case 'load':
@ -242,7 +242,7 @@ ZeroClipboard.Client.prototype = {
setTimeout( function() { self.receiveEvent('load', null); }, 1 );
return;
}
// firefox on pc needs a "kick" in order to set these in certain cases
if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
var self = this;
@ -250,19 +250,19 @@ ZeroClipboard.Client.prototype = {
this.ready = true;
return;
}
this.ready = true;
this.movie.setText( this.clipText );
this.movie.setHandCursor( this.handCursorEnabled );
break;
case 'mouseover':
if (this.domElement && this.cssEffects) {
this.domElement.addClass('hover');
if (this.recoverActive) this.domElement.addClass('active');
}
break;
case 'mouseout':
if (this.domElement && this.cssEffects) {
this.recoverActive = false;
@ -273,13 +273,13 @@ ZeroClipboard.Client.prototype = {
this.domElement.removeClass('hover');
}
break;
case 'mousedown':
if (this.domElement && this.cssEffects) {
this.domElement.addClass('active');
}
break;
case 'mouseup':
if (this.domElement && this.cssEffects) {
this.domElement.removeClass('active');
@ -287,11 +287,11 @@ ZeroClipboard.Client.prototype = {
}
break;
} // switch eventName
if (this.handlers[eventName]) {
for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
var func = this.handlers[eventName][idx];
if (typeof(func) == 'function') {
// actual function reference
func(this, args);
@ -307,5 +307,5 @@ ZeroClipboard.Client.prototype = {
} // foreach event handler defined
} // user defined handler for event
}
};

View File

@ -38,13 +38,24 @@ zerobin = {
},
get_keys: function(){
var keys = new Array();
for(i=0; i<=localStorage.length; i++){
if(localStorage.key(i) != null)
keys[i] = parseInt(localStorage.key(i),10);
}
return keys.sort(zerobin.numOrdA);
},
/** Get a tinyurl using JSONP */
getTinyURL: function(longURL, success) {
callback = 'zerobin_tiny_url_callback';
window[callback] = function(response){
success(response.tinyurl);
delete window[callback];
};
var api = 'http://json-tinyurl.appspot.com/?url=';
$.getJSON(api + encodeURIComponent(longURL) + '&callback=' + callback);
},
support_localstorage: function(){
if (localStorage){
return true;
@ -91,6 +102,13 @@ zerobin = {
}else{
return 'Sorry your browser does not support LocalStorage, We cannot display your previous pastes.';
}
},
get_paste_content: function(){
var content_clone = '' ;
$("#paste-content li").each(function(index) {
content_clone = content_clone + $(this).text() + '\n';
});
return content_clone;
}
};
@ -145,21 +163,32 @@ if (content && key) {
if (!error) {
$('#short-url').click(function(e) {
e.preventDefault();
$('#short-url').text('Loading short url...');
zerobin.getTinyURL(window.location.toString(), function(tinyurl){
clip.setText(tinyurl);
$('#copy-success').hide();
$('#short-url-success')
.html('Short url: <a href="' + tinyurk + '">' + tinyurk + '</a>')
.show('fadeUp');
$('#short-url').text('Get short url');
});
});
prettyPrint();
/* Setup flash clipboard button */
ZeroClipboard.setMoviePath('/static/js/ZeroClipboard.swf' );
var clip = new ZeroClipboard.Client();
clip.addEventListener('onMouseUp', function(){
clip.setText($('#paste-content').text());
var clip = new ZeroClipboard.Client();
clip.addEventListener('mouseup', function(){
clip.setText(zerobin.get_paste_content());
});
clip.addEventListener('complete', function(){
$('#copy-success').show();
$('#copy-success').show('fadeUp', function(){clip.reposition()});
});
clip.addEventListener('onLoad', function(){
});
clip.glue('clip-button', 'clip-container' );
clip.glue('clip-button');
window.onresize = clip.reposition;
}
@ -192,13 +221,12 @@ $('#content').live('keyup change', function(){
/* Display previous pastes */
$('.previous-pastes .items').html(zerobin.get_pastes());
/* clone a paste */
$('.btn-clone').click(function(e){
e.preventDefault();
var content_clone = '' ;
$("#paste-content li").each(function(index) {
content_clone = content_clone + $(this).text() + '\n';
});
var content_clone = zerobin.get_paste_content();
$('.submit-form').show();
$('.paste-form').remove();
$('#content').val(content_clone);

File diff suppressed because one or more lines are too long

View File

@ -17,12 +17,16 @@
The paste is now in your clipboad
</div>
<div id="short-url-success" class="alert alert-success"></div>
<div class="well paste-form">
<form action="/" method="get" accept-charset="utf-8">
<p>
<span id="clip-container" style="position:relative">
<a id="clip-button">Copy To Clipboard</a>
</span>
<a id="clip-button">Copy To Clipboard</a>
|
<a id="short-url" href=""
target="_blank">Get short url</a>
<span class="paste-option btn-group top">
<button class="btn btn-clone"><i class="icon-camera"></i>&nbsp;Clone</button>
<button class="btn">New Paste</button>