diff --git src/daemon/common/commonTypes.ml src/daemon/common/commonTypes.ml
index a6c0e83..5106406 100644
--- src/daemon/common/commonTypes.ml
+++ src/daemon/common/commonTypes.ml
@@ -540,6 +540,7 @@ and userdb = {
   mutable user_default_group : groupdb option;
   mutable user_mail : string;
   mutable user_commit_dir : string;
+  mutable user_share_dir : string option;
   mutable user_max_concurrent_downloads : int;
 }
 
diff --git src/daemon/common/commonUserDb.ml src/daemon/common/commonUserDb.ml
index 4dc1194..a8ff34e 100644
--- src/daemon/common/commonUserDb.ml
+++ src/daemon/common/commonUserDb.ml
@@ -146,6 +146,7 @@ let default_admin_user = {
   user_default_group = Some (admin_group ());
   user_mail = "";
   user_commit_dir = "";
+  user_share_dir = None;
   user_max_concurrent_downloads = 0;
   }
 
@@ -176,6 +177,13 @@ module UserOption = struct
               get_value "user_commit_dir" value_to_string
             with _ -> ""
           in
+          let usdir = 
+            try
+              match get_value "user_share_dir" value_to_string with
+              | "" -> None
+              | s -> Some s
+            with _ -> None 
+          in
           let umaxdl =
 	    try
               get_value "user_max_concurrent_downloads" value_to_int
@@ -223,6 +231,7 @@ module UserOption = struct
 	    user_default_group = udgroup;
 	    user_mail = umail; 
 	    user_commit_dir = ucdir; 
+      user_share_dir = usdir;
 	    user_max_concurrent_downloads = umaxdl; 
 	  }
 
@@ -236,6 +245,7 @@ module UserOption = struct
         "user_default_group", stringoption_to_value (match user.user_default_group with Some g -> Some g.group_name | None -> None);
         "user_mail", string_to_value user.user_mail;
         "user_commit_dir", string_to_value user.user_commit_dir;
+        "user_share_dir", string_to_value (match user.user_share_dir with None -> "" | Some s -> s);
         "user_max_concurrent_downloads", int_to_value user.user_max_concurrent_downloads;
       ]
 
@@ -251,6 +261,7 @@ login as admin in mldonkey, and use the 'useradd' command.
 user_groups                   = Files belonging to one of these groups can be seen by the user.
 user_default_group            = New downloads by this user will belong to this group.
 user_commit_dir               = Commit files to <incoming>/<user_commit_dir>
+user_share_dir                = Paths inside this directory can be shared/unshared by user.
 user_mail                     = Address used to sent confirmation mails after comitting a download
 user_max_concurrent_downloads = Maximum number of downloads allowed, 0 = unlimited
 "
@@ -293,6 +304,7 @@ let user2_user_add name pass ?(groups = [admin_group_name])
       user_default_group = default_group;
       user_mail = mail;
       user_commit_dir = commit_dir;
+      user_share_dir = None;
       user_max_concurrent_downloads = max_dl;
   } in
   update_user name (Some new_user)
@@ -344,6 +356,16 @@ let user2_user_commit_dir user =
 let user2_user_set_commit_dir user dir =
   user.user_commit_dir <- dir
 
+let user2_user_set_share_dir user dir =
+  let dir = Filename2.normalize dir in
+  user.user_share_dir <- if Filename.is_relative dir then None else Some dir
+
+let user2_user_unset_share_dir user =
+  user.user_share_dir <- None
+
+let user2_print_user_share_dir user =
+  match user.user_share_dir with None -> "" | Some dir -> dir
+
 let admin_user () =
   try
     user2_user_find admin_user_name
@@ -408,6 +430,12 @@ let user2_can_view_file user file_owner file_group =
   | None -> false
   | Some file_group -> List.mem file_group user.user_groups)
 
+let user2_can_control_share user dir =
+  user2_is_admin user ||
+  match user.user_share_dir with
+  | None -> false
+  | Some base -> Filename2.is_subdir (Filename2.normalize dir) base
+
 (*************************************************************************)
 (*                         Hooks                                         *)
 (*************************************************************************)
diff --git src/daemon/driver/driverCommands.ml src/daemon/driver/driverCommands.ml
index 03cdeb9..6aa15d0 100644
--- src/daemon/driver/driverCommands.ml
+++ src/daemon/driver/driverCommands.ml
@@ -2516,8 +2516,10 @@ let _ =
      ), "debug command";
 
     "shares", Arg_none (fun o ->
-	if user2_is_admin o.conn_user.ui_user then begin
         let buf = o.conn_buf in
+        let directories = List.filter (fun sd ->
+          user2_can_control_share o.conn_user.ui_user sd.shdir_dirname) !!shared_directories
+        in
 
         if use_html_mods o then begin
             Printf.bprintf buf "\\<div class=\\\"shares\\\"\\>\\<table class=main cellspacing=0 cellpadding=0\\>
@@ -2578,7 +2580,7 @@ let _ =
 		| Some p -> Printf.sprintf "%d%%" p)
 	        (Unix32.filesystem dir);
             )
-            !!shared_directories;
+            directories;
   
             Printf.bprintf buf "\\</table\\>\\</td\\>\\<tr\\>\\</table\\>\\</div\\>\\<P\\>";
 	    print_option_help o shared_directories;
@@ -2621,35 +2623,34 @@ let _ =
             List.iter (fun sd ->
                 Printf.bprintf buf "  %d %s %s\n"
                 sd.shdir_priority sd.shdir_dirname sd.shdir_strategy)
-            !!shared_directories;
+            directories;
 
           end;
         ""
-	  end
-        else
-	  _s "You are not allowed to list shared directories"
     ), ":\t\t\t\tprint shared directories";
 
     "share", Arg_multiple (fun args o ->
-	if user2_is_admin o.conn_user.ui_user then begin
-        let (prio, arg, strategy) = match args with
+        let (prio, dir, strategy) = match args with
           | [prio; arg; strategy] -> int_of_string prio, arg, strategy
           | [prio; arg] -> int_of_string prio, arg, "only_directory"
           | [arg] -> 0, arg, "only_directory"
           | _  -> failwith "Bad number of arguments"
         in
+        if Unix2.is_directory dir then
+        begin
+        let dir = Filename2.normalize dir in
+	      if user2_can_control_share o.conn_user.ui_user dir then
+        begin
 
         let shdir = {
-            shdir_dirname = arg;
+            shdir_dirname = dir;
             shdir_priority = prio;
             shdir_networks = [];
             shdir_strategy = strategy;
           } in
 
-        if Unix2.is_directory arg then
-	  begin
 	    try
-	      let d = List.find (fun d -> d.shdir_dirname = arg) !!shared_directories in
+	      let d = List.find (fun d -> d.shdir_dirname = dir) !!shared_directories in
 	      let old_prio = d.shdir_priority in
 	      d.shdir_priority <- prio;
 	      Printf.sprintf "prio of %s changed from %d to %d"
@@ -2664,23 +2665,25 @@ let _ =
 		 else "")
 	  end
         else
-          "no such directory"
+          _s "You are not allowed to share this directory"
 	  end
         else
-	  _s "You are not allowed to share directories"
+          _s "No such directory"
     ), "<priority> <dir> [<strategy>] :\tshare directory <dir> with <priority> [and sharing strategy <strategy>]";
 
-    "unshare", Arg_one (fun arg o ->
+    "unshare", Arg_one (fun dir o ->
 
-	if user2_is_admin o.conn_user.ui_user then begin
+      let dir = Filename2.normalize dir in
+    	if user2_can_control_share o.conn_user.ui_user dir then
+      begin
         let found = ref false in
         shared_directories =:= List.filter (fun sd ->
-            let diff = sd.shdir_dirname <> arg in
+            let diff = sd.shdir_dirname <> dir in
             if not diff then begin
 	      found := true;
 	      shared_iter (fun s ->
 	        let impl = as_shared_impl s in
-		  if (Filename.dirname impl.impl_shared_fullname) = arg
+		  if (Filename.dirname impl.impl_shared_fullname) = dir
 		    then shared_unshare s
 	      )
 	    end;
@@ -2693,7 +2696,7 @@ let _ =
           _s "directory already unshared"
 	  end
         else
-	  _s "You are not allowed to unshare directories"
+	  _s "You are not allowed to unshare this directory"
     ), "<dir> :\t\t\t\tunshare directory <dir>";
 
     "upstats", Arg_none (fun o ->
@@ -3277,6 +3280,19 @@ let _ =
 (*************************************************************************)
 
 let _ =
+  let change_user_option personal f = Arg_two (fun user arg o ->
+    if user2_is_admin o.conn_user.ui_user || (personal && o.conn_user.ui_user.user_name = user) then
+	  begin
+	    try
+	      let u = user2_user_find user in
+        let s = f u arg in
+	      print_command_result o (Printf.sprintf "User %s has %s" u.user_name s)
+	    with Not_found -> print_command_result o (Printf.sprintf "User %s does not exist" user)
+	  end
+      else print_command_result o "You are not allowed to change this value";
+  	_s ""
+    )
+  in
   register_commands "Driver/Users" [
 
     "useradd", Arg_multiple (fun args o ->
@@ -3448,46 +3464,30 @@ let _ =
 	_s ""
     ), "<passwd> :\t\t\tchange own password";
 
-    "usermail", Arg_two (fun user mail o ->
-        if user2_is_admin o.conn_user.ui_user
-	  || o.conn_user.ui_user.user_name = user then
-	  begin
-	    try
-	      let u = user2_user_find user in
-	      user2_user_set_mail u mail;
-	      print_command_result o (Printf.sprintf "User %s has new mail %s" user mail)
-	    with Not_found -> print_command_result o (Printf.sprintf "User %s does not exist" user)
-	  end
-        else print_command_result o "You are not allowed to change mail addresses";
-	_s ""
-    ), "<user> <mail> :\t\tchange user mail address";
+    "usermail", change_user_option true (fun u arg ->
+	    user2_user_set_mail u arg;
+      Printf.sprintf "new mail %s" arg
+      ), "<user> <mail> :\t\tchange user mail address";
 
-    "userdls", Arg_two (fun user dls o ->
-        if user2_is_admin o.conn_user.ui_user then
-	  begin
-	    try
-	      let u = user2_user_find user in
-	      user2_user_set_dls u (int_of_string dls);
-	      print_command_result o (Printf.sprintf "User %s has now %s downloads allowed" user (user2_print_user_dls (user2_user_find user)))
-	    with Not_found -> print_command_result o (Printf.sprintf "User %s does not exist" user)
-	  end
-        else print_command_result o "You are not allowed to change this value";
-	_s ""
-    ), "<user> <num> :\t\t\tchange number of allowed concurrent downloads";
+    "userdls", change_user_option false (fun u arg ->
+	    user2_user_set_dls u (int_of_string arg);
+      Printf.sprintf "now %s downloads allowed" (user2_print_user_dls u)
+      ), "<user> <num> :\t\t\tchange number of allowed concurrent downloads";
 
-    "usercommit", Arg_two (fun user dir o ->
-        if user2_is_admin o.conn_user.ui_user
-	  || o.conn_user.ui_user.user_name = user then
-	  begin
-	    try
-	      let u = user2_user_find user in
-	      user2_user_set_commit_dir u dir;
-	      print_command_result o (Printf.sprintf "User %s has new commit dir %s" u.user_name u.user_commit_dir)
-	    with Not_found -> print_command_result o (Printf.sprintf "User %s does not exist" user)
-	  end
-        else print_command_result o "You are not allowed to change this value";
-	_s ""
-    ), "<user> <dir> :\t\tchange user specific commit directory";
+    "usercommit", change_user_option true (fun u arg ->
+	      user2_user_set_commit_dir u arg;
+	      Printf.sprintf "new commit directory %s" u.user_commit_dir
+      ), "<user> <dir> :\t\tchange user specific commit directory";
+
+    "usershare", change_user_option false (fun u arg ->
+        if arg = "" then
+          user2_user_unset_share_dir u
+        else
+  	      user2_user_set_share_dir u arg;
+        match u.user_share_dir with
+        | None -> "no share directory anymore"
+        | Some dir -> Printf.sprintf "new share directory %s" dir
+      ), "<user> <dir> :\t\tchange user controlled share directory (use '' to unset)";
 
     "groupadd", Arg_two (fun group admin o ->
 	let g_admin =
@@ -3583,7 +3583,8 @@ let _ =
               ( Str, "srh", "Member of groups", "Groups" ) ;
               ( Str, "srh", "Default group", "Default group" ) ;
               ( Str, "srh", "Mail address", "Email" ) ;
-              ( Str, "srh", "Commit dir", "Commit dir" ) ;
+              ( Str, "srh", "Commit directory", "Commit dir" ) ;
+              ( Str, "srh", "User controlled share directory", "Share dir" );
               ( Num, "srh ar", "Download quota", "Max DLs" ) ;
               ( Num, "srh ar", "Download count", "DLs" ) ];
 
@@ -3627,6 +3628,7 @@ class=\\\"srb\\\"\\>%s\\</a\\> " user.user_name group.group_name group.group_nam
 		  ("", "sr", user2_print_user_default_group user);
 		  ("", "sr", user.user_mail);
 		  ("", "sr", user.user_commit_dir);
+      ("", "sr", user2_print_user_share_dir user);
 		  ("", "sr ar", user2_print_user_dls user);
 		  ("", "sr ar", string_of_int u_dls)];
             );
@@ -3732,6 +3734,7 @@ class=\\\"sr ac\\\"\\>%s\\</td\\>"
 	    (user2_print_user_default_group user);
 	    user.user_mail;
 	    user.user_commit_dir;
+      (user2_print_user_share_dir user);
 	    (user2_print_user_dls user);
 	    (string_of_int (user2_num_user_dls user));
 	  |] :: !list );
@@ -3745,6 +3748,7 @@ class=\\\"sr ac\\\"\\>%s\\</td\\>"
 	    "Dgroup";
 	    "Email";
 	    "Commit dir";
+      "Share dir";
 	    "Max dls";
 	    "Dls";
 	  |] (List.rev !list);
diff --git src/utils/cdk/filename2.ml src/utils/cdk/filename2.ml
index 7fde536..dfd08ad 100644
--- src/utils/cdk/filename2.ml
+++ src/utils/cdk/filename2.ml
@@ -270,8 +270,17 @@ let temp_file prefix suffix =
     with Sys_error _ as e ->
       if counter >= 1000 then raise e else try_name (counter + 1)
   in try_name 0
-  
-let _ = (* some assertions on these functions *)
+
+let is_subdir ~sub ~dir =
+  let rec loop sub dir =
+    match sub,dir with
+    | _,[] -> true
+    | s::sub, d::dir when s = d -> loop sub dir
+    | _ -> false
+  in
+  loop (path_of_filename sub) (path_of_filename dir)
+
+let () = (* some assertions on these functions *)
   assert (basename "c:\\Program Files\\Toto history.exe" = "Toto history.exe");
   assert (path_of_filename 
       "c:\\Program Files\\Toto history.exe" = 
@@ -281,4 +290,4 @@ let _ = (* some assertions on these functions *)
     [ "home"; "bidule"; "mldonkey folder"; "toto"] );
   assert (path_of_filename 
       "/home//bidule" = ["home"; "bidule"])
-  
+
diff --git src/utils/cdk/filename2.mli src/utils/cdk/filename2.mli
index 28f2094..e34905f 100644
--- src/utils/cdk/filename2.mli
+++ src/utils/cdk/filename2.mli
@@ -87,3 +87,8 @@ val temp_file : string -> string -> string
     or "mlnet_tmp" if the variable is not set.
 *)
 val temp_dir_name : unit -> string
+
+(** @return whether [sub] is subdirectory of [dir]
+  both paths should be normalized *)
+val is_subdir : sub:string -> dir:string -> bool
+
diff --git tools/tests.ml tools/tests.ml
index 73ae4c0..e45fc28 100644
--- tools/tests.ml
+++ tools/tests.ml
@@ -62,6 +62,29 @@ let test_hmac_md5 () =
     assert (t (String.make 16 '\xAA') (String.make 50 '\xDD') "56be34521d144c88dbb8c733f0e8b3f6");
   end
 
+open Filename2
+
+let test_subdir () =
+  test ~s:"subdir" begin fun () ->
+  assert (basename "c:\\Program Files\\Toto history.exe" = "Toto history.exe");
+  assert (path_of_filename 
+      "c:\\Program Files\\Toto history.exe" = 
+    [ "c:"; "Program Files"; "Toto history.exe"] );
+  assert (path_of_filename 
+      "/home/bidule/mldonkey folder/toto" = 
+    [ "home"; "bidule"; "mldonkey folder"; "toto"] );
+  assert (path_of_filename 
+      "/home//bidule" = ["home"; "bidule"]);
+  assert (path_of_filename 
+      "/home//bidule/" = ["home"; "bidule"]);
+  assert (is_subdir "/home/user/incoming" "/home/user");
+  assert (is_subdir "/home/user/incoming" "/home/user/");
+  assert (is_subdir "/home/user/incoming" "/home/user/incoming");
+  assert (is_subdir "/home/user/incoming" "/home/user/incoming/");
+  assert (is_subdir "/home/user/incoming/" "/home/user/incoming");
+  assert (not (is_subdir "/home/user/incoming" "/home/user/inc"));
+  end
+
 let () =
 (*   let _ = Ip.addr_of_string "dchub://83.102.255.226" in *)
 (*   let _ = Url.of_string "/submit?q=dcn+dchub://example.com+411" in *)
@@ -69,5 +92,6 @@ let () =
   test_shorten ();
   test_dc_parse ();
   test_hmac_md5 ();
+  test_subdir ();
   pr "Tests finished";
   ()
