diff --git account_prefs_inc.php account_prefs_inc.php
index fa05b13..b975cf0 100644
--- account_prefs_inc.php
+++ account_prefs_inc.php
@@ -304,10 +304,61 @@
 </tr>
 </table>
 </form>
-</div>
 
 <br />
 
+<?php
+	if ( access_has_global_level ( config_get( 'show_avatar_threshold'), $p_user_id ) ) {
+		collapse_open( 'upload_form' );
+?>
+<form method="post" enctype="multipart/form-data" action="account_prefs_avatar_add.php">
+<table class="width75" cellspacing="1">
+<tr>
+	<td class="form-title">
+<?php
+		collapse_icon( 'upload_form' );
+		echo lang_get( 'upload_avatar' ) ?>
+	</td>
+	<td>
+		<?php print_avatar ( $p_user_id ); ?>
+	</td>
+</tr>
+<tr class="row-1">
+	<td class="category" width="15%">
+		<?php echo lang_get( 'select_file' ) ?><br />
+		<?php echo '<span class="small">(' . lang_get( 'max_file_size' ) . ': ' . number_format( $t_max_file_size/1000 ) . 'k)</span>'?>
+	</td>
+	<td width="85%">
+		<input type="hidden" name="avatar_id" value="<?php echo $p_user_id ?>" />
+		<input type="hidden" name="max_file_size" value="<?php echo $t_max_file_size ?>" />
+		<input name="file" type="file" size="40" />
+		<input type="submit" class="button" value="<?php echo lang_get( 'upload_file_button' ) ?>" />
+	</td>
+</tr>
+</table>
+</form>
+<?php
+	collapse_closed( 'upload_form' );
+?>
+<table class="width75" cellspacing="1">
+<tr>
+	<td class="form-title">
+		<?php
+			collapse_icon( 'upload_form' );
+			echo lang_get( 'upload_avatar' ) ?>
+	</td>
+	<td>
+		<?php print_avatar ( $p_user_id ); ?>
+	</td>
+</tr>
+</table>
+
+<?php
+		collapse_end( 'upload_form' );
+	}
+?>
+</div>
+
 <div class="border-center">
 	<form method="post" action="account_prefs_reset.php">
 	<input type="hidden" name="user_id" value="<?php echo $p_user_id ?>" />
diff --git core/file_api.php core/file_api.php
index 5bcc0a7..0ea495e 100644
--- core/file_api.php
+++ core/file_api.php
@@ -282,19 +282,18 @@ document.getElementById( span ).style.display = displayType;
 			}
 		}
 	}
-	# --------------------
-	# delete all files that are associated with the given bug
-	function file_delete_attachments( $p_bug_id ) {
-		$c_bug_id = db_prepare_int( $p_bug_id );
 
-		$t_bug_file_table = config_get( 'mantis_bug_file_table' );
+	function file_delete_many( $p_item_id, $p_item_type ) {
+		$c_item_id = db_prepare_int( $p_item_id );
+
+		$t_item_file_table = config_get( 'mantis_'.$p_item_type.'_file_table' );
 
 		$t_method = config_get( 'file_upload_method' );
 
 		# Delete files from disk
 		$query = "SELECT diskfile, filename
-				FROM $t_bug_file_table
-				WHERE bug_id='$c_bug_id'";
+				FROM $t_item_file_table
+				WHERE ".$p_item_type."_id='$c_item_id'";
 		$result = db_query( $query );
 
 		$file_count = db_num_rows( $result );
@@ -302,6 +301,20 @@ document.getElementById( span ).style.display = displayType;
 			return true;
 		}
 
+		/*
+		 *  the part below could be replaced by
+		 *  
+		 * 	for ( $i = 0 ; $i < $file_count ; $i++ ) {
+		 *		$row = db_fetch_array( $result );
+		 *		file_delete ( $row['id'], $p_item_type );
+		 * 	}
+		 *  
+		 *  but that would result in quite a lot of queries
+		 *  and ftp connections etc. on the other hand the
+		 *  history would be updated and the operation would
+		 *  be somewhat more fine grained
+		 */
+		
 		if ( ( DISK == $t_method ) || ( FTP == $t_method ) ) {
 			# there may be more than one file
 			$ftp = 0;
@@ -325,52 +338,26 @@ document.getElementById( span ).style.display = displayType;
 		}
 
 		# Delete the corresponding db records
-		$query = "DELETE FROM $t_bug_file_table
-				  WHERE bug_id='$c_bug_id'";
+		$query = "DELETE FROM $t_item_file_table
+				  WHERE ".$p_item_type."_id='$c_item_id'";
 		$result = db_query( $query );
 
 		# db_query() errors on failure so:
 		return true;
 	}
+	
+	# --------------------
+	# delete all files that are associated with the given bug
+	function file_delete_attachments( $p_bug_id ) {
+		return file_delete_many ( $p_bug_id, 'bug' );
+	}
 	# --------------------
 	function file_delete_project_files( $p_project_id ) {
-		$t_project_file_table	= config_get( 'mantis_project_file_table' );
-		$t_method				= config_get( 'file_upload_method' );
-
-		# Delete the file physically (if stored via DISK or FTP)
-		if ( ( DISK == $t_method ) || ( FTP == $t_method ) ) {
-			# Delete files from disk
-			$query = "SELECT diskfile, filename
-					FROM $t_project_file_table
-					WHERE project_id=$p_project_id";
-			$result = db_query( $query );
-
-			$file_count = db_num_rows( $result );
-
-			$ftp = 0;
-			if ( FTP == $t_method ) {
-				$ftp = file_ftp_connect();
-			}
-
-			for ( $i = 0 ; $i < $file_count ; $i++ ) {
-				$row = db_fetch_array( $result );
-
-				file_delete_local ( $row['diskfile'] );
-
-				if ( FTP == $t_method ) {
-					file_ftp_delete ( $ftp, $row['diskfile'] );
-				}
-			}
-
-			if ( FTP == $t_method ) {
-				file_ftp_disconnect( $ftp );
-			}
-		}
-
-		# Delete the corresponding db records
-		$query = "DELETE FROM $t_project_file_table
-				WHERE project_id=$p_project_id";
-		$result = db_query($query);
+		return file_delete_many ( $p_project_id, 'project' );
+	}
+	# --------------------
+	function file_delete_avatar_files( $p_avatar_id ) {
+		return file_delete_many ( $p_avatar_id, 'avatar' );
 	}
 	# --------------------
 	# Delete all cached files that are older than configured number of days.
@@ -550,15 +537,15 @@ document.getElementById( span ).style.display = displayType;
 
 	# --------------------
 	# Return true if the file name identifier is unique, false otherwise
-	function file_is_name_unique( $p_name, $p_bug_id ) {
-		$t_file_table = config_get( 'mantis_bug_file_table' );
+	function file_is_name_unique( $p_name, $p_item_id, $p_item_type ) {
+		$t_file_table = config_get( 'mantis_'.$p_item_type.'_file_table' );
 
 		$c_name = db_prepare_string( $p_name );
-		$c_bug = db_prepare_string( $p_bug_id );
+		$c_item = db_prepare_string( $p_item_id );
 
 		$query = "SELECT COUNT(*)
 				  FROM $t_file_table
-				  WHERE filename='$c_name' and bug_id=$c_bug";
+				  WHERE filename='$c_name' and ".$p_item_type."_id=$c_item";
 		$result = db_query( $query );
 		$t_count = db_result( $result );
 
@@ -598,24 +585,27 @@ document.getElementById( span ).style.display = displayType;
 			trigger_error( ERROR_FILE_NOT_ALLOWED, ERROR );
 		}
 
-		if ( !file_is_name_unique( $p_file_name, $p_bug_id ) ) {
+		if ( !file_is_name_unique( $p_file_name, $p_bug_id, $p_table ) ) {
 			trigger_error( ERROR_DUPLICATE_FILE, ERROR );
 		}
-
+		
+		$t_project_id	= helper_get_current_project();
+		$t_bug_id		= 0;
 		if ( 'bug' == $p_table ) {
 			$t_project_id	= bug_get_field( $p_bug_id, 'project_id' );
 			$t_bug_id		= bug_format_id( $p_bug_id );
-		} else {
-			$t_project_id	= helper_get_current_project();
-			$t_bug_id		= 0;
+		} else if ( 'avatar' == $p_table ) {
+			$t_user_id = auth_get_current_user_id ();
+			$t_bug_id  = 0; 
 		}
-
+		
 		# prepare variables for insertion
 		$c_bug_id		= db_prepare_int( $p_bug_id );
-		$c_project_id		= db_prepare_int( $t_project_id );
+		$c_project_id	= db_prepare_int( $t_project_id );
+		$c_user_id		= db_prepare_int( $t_user_id );
 		$c_file_type	= db_prepare_string( $p_file_type );
-		$c_title = db_prepare_string( $p_title );
-		$c_desc = db_prepare_string( $p_desc );
+		$c_title 		= db_prepare_string( $p_title );
+		$c_desc 		= db_prepare_string( $p_desc );
 
 		if( $t_project_id == ALL_PROJECTS ) {
 			$t_file_path = config_get( 'absolute_path_default_upload_folder' );
@@ -629,7 +619,13 @@ document.getElementById( span ).style.display = displayType;
 		$c_file_path = db_prepare_string( $t_file_path );
 		$c_new_file_name = db_prepare_string( $p_file_name );
 
-		$t_file_hash = ( 'bug' == $p_table ) ? $t_bug_id : config_get( 'document_files_prefix' ) . '-' . $t_project_id;
+		$t_file_hash = config_get( 'document_files_prefix' ) . '-' . $t_project_id;
+		if ( 'avatar' == $p_table ) {
+			$t_file_hash = config_get ( 'avatar_files_prefix' ) . '-' . $t_user_id;
+		} else if ( 'bug' == $p_table ) {
+			$t_file_hash = $t_bug_id;
+		}
+
 		$t_disk_file_name = $t_file_path . file_generate_unique_name( $t_file_hash . '-' . $p_file_name, $t_file_path );
 		$c_disk_file_name = db_prepare_string( $t_disk_file_name );
 
@@ -676,8 +672,14 @@ document.getElementById( span ).style.display = displayType;
 		}
 
 		$t_file_table	= config_get( 'mantis_' . $p_table . '_file_table' );
-		$c_id = ( 'bug' == $p_table ) ? $c_bug_id : $c_project_id;
-					
+		$c_id = $c_project_id;
+		if ( 'bug' == $p_table ) {
+			$c_id = $c_bug_id;
+		} else if ( 'avatar' == $p_table ) {
+			file_delete_avatar_files ( $c_user_id, 'avatar');
+			$c_id = $c_user_id;
+		}
+		
 		$query = "INSERT INTO $t_file_table
 						(" . $p_table . "_id, title, description, diskfile, filename, folder, filesize, file_type, date_added, content)
 					  VALUES
diff --git core/print_api.php core/print_api.php
index cf8dd42..b9016e5 100644
--- core/print_api.php
+++ core/print_api.php
@@ -130,7 +130,8 @@
 				$t_avatar_url = $t_avatar[0];
 				$t_width = $t_avatar[1];
 				$t_height = $t_avatar[2];
-				echo '<a rel="nofollow" href="http://site.gravatar.com">' .
+				$t_href = $t_avatar[3];
+				echo '<a rel="nofollow" href="'. $t_href .'">' .
 					'<img class="avatar" src="' . $t_avatar_url . '" alt="User avatar"' .
 					' width="' . $t_width . '" height="' . $t_height . '" /></a>';
 			}
diff --git core/user_api.php core/user_api.php
index 23dbb45..a31ae19 100644
--- core/user_api.php
+++ core/user_api.php
@@ -491,6 +491,21 @@
 	}
 
 	# --------------------
+	# delete avatars for the specified user
+	# returns true when successfully deleted
+	function user_delete_avatar( $p_user_id ) {
+		$c_user_id = db_prepare_int($p_user_id);
+
+		user_ensure_unprotected( $p_user_id );
+
+		file_delete_avatar_files ( $p_user_id );
+		
+		user_clear_cache( $p_user_id );
+
+		return true;
+	}
+	
+	# --------------------
 	# delete a user account (account, profiles, preferences, project-specific access levels)
 	# returns true when the account was successfully deleted
 	function user_delete( $p_user_id ) {
@@ -499,6 +514,9 @@
 
 		user_ensure_unprotected( $p_user_id );
 
+		# Remove the user avatar
+		user_delete_avatar ( $p_user_id );
+		
 		# Remove associated profiles
 		user_delete_profiles( $p_user_id );
 
@@ -649,34 +667,66 @@
 	/**
 	* Return the user avatar image URL
 	* in this first implementation, only gravatar.com avatars are supported
-	* @return array|bool an array( URL, width, height ) or false when the given user has no avatar
+	* @return array|bool an array( URL, width, height, href ) or false when the given user has no avatar
 	*/
 	function user_get_avatar( $p_user_id ) {
-		$t_email = strtolower( user_get_email( $p_user_id ) );
-		if ( is_blank( $t_email ) ) {
-			$t_result = false;
-		} else {
-			$t_default_image = config_get( 'default_avatar' );
-			$t_size = 80;
-
-			$t_use_ssl = false;
-			if ( isset( $_SERVER['HTTPS'] ) && ( strtolower( $_SERVER['HTTPS'] ) != 'off' ) ) {
-				$t_use_ssl = true;
+		/*
+		 *  First try to find a local avatar
+		 *  - if it is found then this one is delivered
+		 *  - if it isn't found proceed with original handling
+		 *  - if you disabled local avatars then anyway try to deliver the gravatar
+		 */
+		$t_avatar_type = strtolower ( config_get ( 'avatar_type' ) );
+		$t_found_local_avatar = false;
+
+		if ( $t_avatar_type == 'local' ) {
+			$c_user_id = db_prepare_int ( $p_user_id );
+			$t_avatar_table = config_get ( 'mantis_avatar_file_table' );
+			$query = "SELECT id
+					  FROM $t_avatar_table
+					  WHERE avatar_id='$c_user_id'";
+			$t_db_result = db_query ( $query );
+			/*
+			 * theoretically you could have more than one local avatar
+			 * but for now only one is allowed.
+			 */
+			if ( 1 == db_num_rows( $t_db_result )) {
+				$t_found_local_avatar = true;
+				$t_avatar_id = db_result ( $t_db_result );
+				$t_avatar_url = "file_download.php?type=avatar&amp;file_id=".$t_avatar_id;
+				$t_avatar_ref = "manage_user_edit_page.php?user_id=".$p_user_id;
+				$t_size = 80;
+				$t_result = array( $t_avatar_url, $t_size, $t_size, $t_avatar_ref );
 			}
-
-			if ( !$t_use_ssl ) {
-				$t_gravatar_domain = 'http://www.gravatar.com/';
+		}
+		
+		if ( ! $t_found_local_avatar ) {
+			$t_email = strtolower( user_get_email( $p_user_id ) );
+			if ( is_blank( $t_email ) ) {
+				$t_result = false;
 			} else {
-				$t_gravatar_domain = 'https://secure.gravatar.com/';
+				$t_default_image = config_get( 'default_avatar' );
+				$t_size = 80;
+	
+				$t_use_ssl = false;
+				if ( isset( $_SERVER['HTTPS'] ) && ( strtolower( $_SERVER['HTTPS'] ) != 'off' ) ) {
+					$t_use_ssl = true;
+				}
+	
+				if ( !$t_use_ssl ) {
+					$t_gravatar_domain = 'http://www.gravatar.com/';
+				} else {
+					$t_gravatar_domain = 'https://secure.gravatar.com/';
+				}
+	
+				$t_avatar_url = $t_gravatar_domain . 'avatar.php?gravatar_id=' . md5( $t_email ) .
+					'&amp;default=' . urlencode( $t_default_image ) .
+					'&amp;size=' . $t_size .
+					'&amp;rating=G';
+				$t_result = array( $t_avatar_url, $t_size, $t_size, "http://site.gravatar.com" );
 			}
-
-			$t_avatar_url = $t_gravatar_domain . 'avatar.php?gravatar_id=' . md5( $t_email ) .
-				'&amp;default=' . urlencode( $t_default_image ) .
-				'&amp;size=' . $t_size .
-				'&amp;rating=G';
-			$t_result = array( $t_avatar_url, $t_size, $t_size );
 		}
-
+		
 		return $t_result;
 	}
 
diff --git file_download.php file_download.php
index 8074cc4..6d2f29e 100644
--- file_download.php
+++ file_download.php
@@ -37,27 +37,18 @@
 	$f_file_id	= gpc_get_int( 'file_id' );
 	$f_type		= gpc_get_string( 'type' );
 
+	$t_allowed_tables = array ( 'bug', 'doc', 'avatar' );
+	if ( ! in_array ($f_type, $t_allowed_tables) ) {
+		access_denied();
+	}
+	
 	$c_file_id = (integer)$f_file_id;
 
-	# we handle the case where the file is attached to a bug
-	# or attached to a project as a project doc.
 	$query = '';
-	switch ( $f_type ) {
-		case 'bug':
-			$t_bug_file_table = config_get( 'mantis_bug_file_table' );
-			$query = "SELECT *
-				FROM $t_bug_file_table
-				WHERE id='$c_file_id'";
-			break;
-		case 'doc':
-			$t_project_file_table = config_get( 'mantis_project_file_table' );
-			$query = "SELECT *
-				FROM $t_project_file_table
-				WHERE id='$c_file_id'";
-			break;
-		default:
-			access_denied();
-	}
+	$t_file_table = config_get( 'mantis_'.$f_type.'_file_table' );
+	$query = "SELECT *
+		FROM $t_file_table
+		WHERE id='$c_file_id'";
 	$result = db_query( $query );
 	$row = db_fetch_array( $result );
 	extract( $row, EXTR_PREFIX_ALL, 'v' );
diff --git lang/strings_english.txt lang/strings_english.txt
index 68091d6..f637024 100644
--- lang/strings_english.txt
+++ lang/strings_english.txt
@@ -1452,3 +1452,6 @@ $s_show_graph = 'Show Graph';
 $s_graph_page = 'Graph Bug History';
 $s_graph_bug_page_link = 'Graph';
 
+# avatar
+$s_upload_avatar = 'Upload avatar';
+$s_avatar = 'Avatar';
diff --git manage_user_page.php manage_user_page.php
index 68e6594..8d93782 100644
--- manage_user_page.php
+++ manage_user_page.php
@@ -263,6 +263,9 @@ for ($i=0;$i<$new_user_count;$i++) {
 		<?php print_manage_user_sort_link(  'manage_user_page.php', lang_get( 'last_visit' ), 'last_visit', $c_dir, $c_sort, $c_hide ) ?>
 		<?php print_sort_icon( $c_dir, $c_sort, 'last_visit' ) ?>
 	</td>
+	<td>
+		<?php echo lang_get( 'avatar' ); ?>
+	</td>
 </tr>
 <?php
 	for ($i=0;$i<$user_count;$i++) {
@@ -292,6 +295,7 @@ for ($i=0;$i<$new_user_count;$i++) {
         </td>
 	<td><?php echo $u_date_created ?></td>
 	<td><?php echo $u_last_visit ?></td>
+	<td><?php print_avatar ( $u_id ); ?></td>
 </tr>
 <?php
 	}  # end for
