diff --git a/avatar.php b/avatar.php
index a54aad3b2a626967433ee0f8a5953ab50258a76d..c860ad9e36936c4a16989eeeff3c66cf5f18d798 100644
--- a/avatar.php
+++ b/avatar.php
@@ -36,26 +36,40 @@ if ($_SERVER['REQUEST_METHOD'] === "GET") {
 
 	// Select an image from own files
 	if (isset($_POST['path'])) {
-		//SECURITY TODO does this fully eliminate directory traversals?
 		$path = stripslashes($_POST['path']);
 		$avatar = OC::$SERVERROOT.'/data/'.$user.'/files'.$path;
 	}
+
+	if (isset($_POST['crop'])) {
+		$crop = json_decode($_POST['crop'], true);
+		if (!isset($path)) {
+			// TODO get path to temporarily saved uploaded-avatar
+		}
+		$image = new \OC_Image($avatar);
+		$image->crop($x, $y, $w, $h);
+		$avatar = $image->data();
+	}
+
 	// Upload a new image
-	elseif (!empty($_FILES)) {
+	if (!empty($_FILES)) {
 		$files = $_FILES['files'];
 		if ($files['error'][0] === 0) {
 			$avatar = file_get_contents($files['tmp_name'][0]);
 			unlink($files['tmp_name'][0]);
+			// TODO make the tmp_name reusable, if the uploaded avatar is not square
 		}
-	} else {
-	        OC_JSON::error();
 	}
 
 	try {
 		\OC_Avatar::set($user, $avatar);
 		OC_JSON::success();
+	} catch (\OC\NotSquareException $e) {
+		$tmpname = \OC_Util::generate_random_bytes(10);
+		// TODO Save the image temporarily here
+		// TODO add a cronjob that cleans up stale tmpimages
+		OC_JSON::error(array("data" => array("message" => "notsquare", "tmpname" => $tmpname) ));
 	} catch (\Exception $e) {
-		OC_JSON::error(array("data" => array ("message" => $e->getMessage()) ));
+		OC_JSON::error(array("data" => array("message" => $e->getMessage()) ));
 	}
 } elseif ($_SERVER['REQUEST_METHOD'] === "DELETE") {
 	$user = OC_User::getUser();
diff --git a/lib/avatar.php b/lib/avatar.php
index 86be0ea26356a42830e83d89986a04ebc4ad6bc3..9ab905c852e52b3cd9a54401f83ee938c74016d2 100644
--- a/lib/avatar.php
+++ b/lib/avatar.php
@@ -26,7 +26,7 @@ class OC_Avatar {
 			$ext = 'png';
 		} else {
 			return false;
-                }
+		}
 
 		$avatar = new OC_Image($view->file_get_contents('avatar.'.$ext));
 		$avatar->resize($size);
@@ -38,7 +38,8 @@ class OC_Avatar {
 	 * @param $user string user to set the avatar for
 	 * @param $data mixed imagedata or path to set a new avatar
 	 * @throws Exception if the provided file is not a jpg or png image
-	 * @throws Exception if the provided image is not valid, or not a square
+	 * @throws Exception if the provided image is not valid
+	 * @throws \OC\NotSquareException if the image is not square
 	 * @return true on success
 	*/
 	public static function set ($user, $data) {
@@ -52,9 +53,13 @@ class OC_Avatar {
 			throw new \Exception($l->t("Unknown filetype"));
 		}
 
-		if (!( $img->valid() && ($img->height() === $img->width()) )) {
+		if (!$img->valid()) {
 			$l = \OC_L10N::get('lib');
-			throw new \Exception($l->t("Invalid image, or the provided image is not square"));
+			throw new \Excpeption($l->t("Invalid image"));
+		}
+
+		if (!($img->height() === $img->width())) {
+			throw new \OC\NotSquareException();
 		}
 
 		$view->unlink('avatar.jpg');
diff --git a/lib/notsquareexception.php b/lib/notsquareexception.php
new file mode 100644
index 0000000000000000000000000000000000000000..03dba8fb25f216feb72519d9985e30e735c1d2c7
--- /dev/null
+++ b/lib/notsquareexception.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2013 Christopher Schäpers <christopher@schaepers.it>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC;
+
+class NotSquareException extends \Exception {
+}
diff --git a/lib/public/avatar.php b/lib/public/avatar.php
index 768d292346f928ff90f20e274c6a0b0e74e5fcb0..55eff57d1613014fbc69e6f3717562577b4689fd 100644
--- a/lib/public/avatar.php
+++ b/lib/public/avatar.php
@@ -12,8 +12,4 @@ class Avatar {
 	public static function get ($user, $size = 64) {
 		return \OC_Avatar::get($user, $size);
 	}
-
-	public static function getMode () {
-		return \OC_Avatar::getMode();
-	}
 }
diff --git a/settings/css/settings.css b/settings/css/settings.css
index e6ced0e375a13fdb12290d1798c92827116202da..a2c3eaf6263c64666b4cdfc243e319f9963bad05 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -21,6 +21,8 @@ input#openid, input#webdav { width:20em; }
 input#identity { width:20em; }
 #email { width: 17em; }
 
+#avatar .warning { width: 350px; }
+
 .msg.success{ color:#fff; background-color:#0f0; padding:3px; text-shadow:1px 1px #000; }
 .msg.error{ color:#fff; background-color:#f00; padding:3px; text-shadow:1px 1px #000; }
 
diff --git a/settings/js/personal.js b/settings/js/personal.js
index dd2d15052d15d09f1b62ec8c5691fd89c4e8f940..eaf90636d3513d398f9868b2209e87577a936267 100644
--- a/settings/js/personal.js
+++ b/settings/js/personal.js
@@ -45,17 +45,57 @@ function changeDisplayName(){
 }
 
 function selectAvatar (path) {
-	$.post(OC.filePath('', '', 'avatar.php'), {path: path}, function(data) {
-		if (data.status === "success") {
-			updateAvatar();
-		} else {
-			OC.dialogs.alert(data.data.message, t('core', "Error"));
-		}
-	});
+	$.post(OC.filePath('', '', 'avatar.php'), {path: path}, avatarResponseHandler);
 }
 
 function updateAvatar () {
-	$('#avatar img').attr('src', $('#avatar img').attr('src') + '#');
+	$avatarimg = $('#avatar img');
+	$avatarimg.attr('src', $avatarimg.attr('src') + '#');
+}
+
+function showAvatarCropper() {
+	OC.dialogs.message('', t('settings', 'Crop'), undefined, OCdialogs.OK_BUTTON, sendCropData);
+	var $dialog = $('#oc-dialog-'+(OC.dialogs.dialogs_counter-1)+'-content');
+	var cropper = new Image();
+	$(cropper).load(function() {
+		$(this).attr('id', 'cropper');
+		$('#oc-dialog-'+(OC.dialogs.dialogs_counter-1)+'-content').html(this);
+		$(this).Jcrop({
+			onChange: saveCoords,
+			onSelect: saveCoords,
+			aspectRatio: 1
+		});
+	}).attr('src', OC.filePath('', '', 'avatar.php')+"?user="+OC.currentUser+"&size=512&tmp="+$('#avatar').data('tmpname'));
+}
+
+function sendCropData() {
+	var tmp = $('#avatar').data('tmpname');
+	var cropperdata = $('#cropper').data();
+	var data = {
+		x: cropperdata.x,
+		y: cropperdata.y,
+		w: cropperdata.w,
+		h: cropperdata.h
+	};
+	$.post(OC.filePath('', '', 'avatar.php'), {tmp:tmp, crop: data}, avatarResponseHandler);
+}
+
+function saveCoords(c) {
+	$('#cropper').data(c);
+}
+
+function avatarResponseHandler(data) {
+	$warning = $('#avatar .warning');
+	$warning.hide();
+	if (data.status === "success") {
+		updateAvatar();
+	} else if (data.data.message === "notsquare") {
+		$('#avatar').data('tmpname', data.data.tmpname);
+		showAvatarCropper();
+	} else {
+		$warning.show();
+		$warning.text(data.data.message);
+	}
 }
 
 $(document).ready(function(){
@@ -149,11 +189,7 @@ $(document).ready(function(){
 
 	var uploadparms = {
 		done: function(e, data) {
-			if (data.result.status === "success") {
-				updateAvatar();
-			} else {
-				OC.dialogs.alert(data.result.data.message, t('core', "Error"));
-			}
+			avatarResponseHandler(data.result);
 		}
 	};
 
diff --git a/settings/personal.php b/settings/personal.php
index d109d33e4bd6161355950c49576308b6080a214b..33c78c0467f69b68d2df6fc7520fd57f7eaa3d98 100644
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -16,6 +16,8 @@ OC_Util::addStyle( 'settings', 'settings' );
 OC_Util::addScript( '3rdparty', 'chosen/chosen.jquery.min' );
 OC_Util::addStyle( '3rdparty', 'chosen' );
 \OC_Util::addScript('files', 'jquery.fileupload');
+\OC_Util::addScript('3rdparty/Jcrop', 'jquery.Jcrop.min');
+\OC_Util::addStyle('3rdparty/Jcrop', 'jquery.Jcrop.min');
 OC_App::setActiveNavigationEntry( 'personal' );
 
 $storageInfo=OC_Helper::getStorageInfo();
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index 7cd5361a92431b01762440235f922aab25dca76b..5db28779b556337c6cb5330bfaebf4ffaab9c37b 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -88,6 +88,7 @@ if($_['passwordChangeSupported']) {
 		<legend><strong><?php p($l->t('Profile Image')); ?></strong></legend>
 		<img src="<?php print_unescaped(link_to('', 'avatar.php').'?user='.OC_User::getUser().'&size=128'); ?>"><br>
 		<em><?php p($l->t('Has to be square and either PNG or JPG')); ?></em><br>
+		<div class="warning hidden"></div>
 		<div class="inlineblock button" id="uploadavatarbutton"><?php p($l->t('Upload new')); ?></div>
 		<input type="file" class="hidden" name="files[]" id="uploadavatar">
 		<div class="inlineblock button" id="selectavatar"><?php p($l->t('Select new from files')); ?></div>