[glom] Add or check for users when activating or deactivating network sharing.



commit df6311c399766fce8148343feb9f20ed01a5d0a4
Author: Murray Cumming <murrayc murrayc com>
Date:   Sun Apr 19 20:17:26 2009 +0200

    Add or check for users when activating or deactivating network sharing.
    
    * glom/application.cc: init_menus_file(), update_network_shared_ui():
    Make the network-sharing menu item insensitive if not in developer mode.
    on_document_load(): Some cleanup - mostly indentation changes, I think.
    
    * glom/dialog_connection.[h|cc]: Added set_confirm_existing_user_note():
    to change the text so we can also use this to check that the user knows
    a password before switching to network-sharing mode.
    * glom/glom_developer.glade: dialog_new_self_hosted_connection:
    Change the text to make it also appropriate for asking for a first
    password when first switching to network-sharing.
    
    * glom/glom_privs.[h|cc]: Added get_default_developer_user_exists(),
    get_developer_user_exists_with_password() and
    get_default_developer_user_name().
    * glom/libglom/connectionpool.cc: connect(): Catch exceptions from
    the update_meta_store_*() methods.
    set_user(), set_password(), set_database(): Completely invalidate the
    connection when these change, forcing a reconnect later.
    * glom/mode_design/users/dialog_users_list.cc
     on_button_user_remove(), on_button_user_delete(), on_button_user_new():
    Move SQL into:
    * glom/base_db.[h|cc]: add_user(), reomve_user(), remove_user_from_group().
    Added set_database_owner_user(), Glom.disable_user():
    (Glom.type_list_lookups): (Glom.add_standard_groups,
    
    * glom/libglom/connectionpool_backends/postgres_self.cc
    DEFAULT_CONFIG_PG_HBA_LOCAL: For non-network-shared instances, use
    trust authentication (though we still need to specify a user).
    * glom/libglom/connectionpool_backends/postgres.cc: create_auth_string():
    Avoid an odd auth_string if the username or password are emtpy, though
    I don't think we actually do this.
    
    * glom/frame_glom.[h|cc]: connection_request_initial_password(),
    Added this, moving some exisiting code here, to avoid duplication.
    connection_request_password_and_choose_new_database_name(),
    connection_request_password_and_attempt(): Use the default user/password
    if we are not network shared.
    on_menu_file_toggle_share(): Check that we are in developer mode.
    Before starting network sharing, check that the user knows a
    current developer password, or ask for a new one.
    Before stopping network sharing, add/activate the default user.
---
 glom/application.cc                                |   75 ++--
 glom/base_db.cc                                    |  133 ++++++
 glom/base_db.h                                     |   22 +
 glom/dialog_connection.cc                          |   18 +-
 glom/dialog_connection.h                           |    7 +
 glom/frame_glom.cc                                 |  438 ++++++++++++++------
 glom/frame_glom.h                                  |   23 +-
 glom/glom_developer.glade                          |    2 +-
 glom/glom_privs.cc                                 |   38 ++
 glom/glom_privs.h                                  |   32 ++
 glom/libglom/connectionpool.cc                     |   18 +-
 glom/libglom/connectionpool_backends/postgres.cc   |   18 +-
 .../connectionpool_backends/postgres_self.cc       |    8 +-
 glom/main.cc                                       |    4 +
 glom/mode_design/users/dialog_users_list.cc        |   30 +--
 15 files changed, 641 insertions(+), 225 deletions(-)

diff --git a/glom/application.cc b/glom/application.cc
index a149935..058eba8 100644
--- a/glom/application.cc
+++ b/glom/application.cc
@@ -20,8 +20,8 @@
 
 #include <config.h> //For VERSION, GLOM_ENABLE_CLIENT_ONLY, GLOM_ENABLE_SQLITE
 
-#include "application.h"
-#include "dialog_existing_or_new.h"
+#include <glom/application.h>
+#include <glom/dialog_existing_or_new.h>
 
 #include <glom/dialog_progress_creating.h>
 
@@ -33,6 +33,7 @@
 
 #include <glom/utils_ui.h>
 #include <glom/glade_utils.h>
+#include <glom/glom_privs.h>
 
 #include <cstdio>
 #include <memory> //For std::auto_ptr<>
@@ -332,6 +333,7 @@ void App_Glom::init_menus_file()
   m_connection_toggleaction_network_shared = 
     m_toggleaction_network_shared->signal_toggled().connect(
       sigc::mem_fun(*this, &App_Glom::on_menu_file_toggle_share) );
+  m_listDeveloperActions.push_back(m_toggleaction_network_shared);
 
   m_refFileActionGroup->add(Gtk::Action::create("GlomAction_Menu_File_Print", Gtk::Stock::PRINT));
   m_refFileActionGroup->add(Gtk::Action::create("GlomAction_File_Print", _("_Standard")),
@@ -1048,56 +1050,49 @@ bool App_Glom::on_document_load()
 
         //Attempt to connect to the specified database:
         bool test = false;
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-        try
-#else
-        std::auto_ptr<ExceptionConnection> error;
-#endif
-        {
+
 #ifndef GLOM_ENABLE_CLIENT_ONLY
-          if(is_example)
-          {
-            //The user has already had the chance to specify a new filename and database name.
-            test = m_pFrame->connection_request_password_and_choose_new_database_name();
-          }
-          else
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-            //Ask for the username/password and connect:
-            //Note that m_temp_username and m_temp_password are set if 
-            //we already asked for them when getting the document over the network: 
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-            test = m_pFrame->connection_request_password_and_attempt(m_temp_username, m_temp_password);
-#else
-            test = m_pFrame->connection_request_password_and_attempt(m_temp_username, m_temp_password, error);
-#endif
-            m_temp_username = Glib::ustring();
-            m_temp_password = Glib::ustring();
-        }
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-        catch(const ExceptionConnection& ex)
+        if(is_example)
         {
-#else
-        if(error.get())
+          //The user has already had the chance to specify a new filename and database name.
+          test = m_pFrame->connection_request_password_and_choose_new_database_name();
+        }
+        else
+#endif // !GLOM_ENABLE_CLIENT_ONLY
         {
-          const ExceptionConnection& ex = *error.get();
-#endif
-          if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_DATABASE) //This is the only FAILURE_* type that connection_request_password_and_attempt() throws.
+          //Ask for the username/password and connect:
+          //Note that m_temp_username and m_temp_password are set if 
+          //we already asked for them when getting the document over the network:
+
+          //Use the default username/password if opening as non network-shared:
+          if(!(pDocument->get_network_shared()))
           {
-#ifndef GLOM_ENABLE_CLIENT_ONLY
+            m_temp_username = Privs::get_default_developer_user_name(m_temp_password);
+          }
+
+          bool database_not_found = false;
+          test = m_pFrame->connection_request_password_and_attempt(database_not_found, m_temp_username, m_temp_password);
+          m_temp_username = Glib::ustring();
+          m_temp_password = Glib::ustring();
+
+          if(!test && database_not_found)
+          {
+            #ifndef GLOM_ENABLE_CLIENT_ONLY
             if(!is_example)
             {
               //The connection to the server is OK, but the database is not there yet.
               Frame_Glom::show_ok_dialog(_("Database Not Found On Server"), _("The database could not be found on the server. Please consult your system administrator."), *this, Gtk::MESSAGE_ERROR);
             }
             else
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-              std::cerr << "App_Glom::on_document_load(): unexpected ExceptionConnection when opening example." << std::endl;
+            #endif // !GLOM_ENABLE_CLIENT_ONLY
+              std::cerr << "App_Glom::on_document_load(): unexpected database_not_found error when opening example." << std::endl;
           }
           else
-            std::cerr << "App_Glom::on_document_load(): unexpected ExceptionConnection failure type." << std::endl;
+            std::cerr << "App_Glom::on_document_load(): unexpected error." << std::endl;
+
         }
 
-        if(!test) //It usually throws an exception instead of returning false.
+        if(!test)
           return false; //Failed. Close the document.
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
@@ -1204,9 +1199,9 @@ void App_Glom::update_network_shared_ui()
   //Show the status in the UI:
   //(get_network_shared() already enforces constraints).
   const bool shared = document->get_network_shared();
+  //TODO: Our use of block() does not seem to work. The signal actually seems to be emitted some time later instead.
   m_connection_toggleaction_network_shared.block(); //Prevent signal handling.
   m_toggleaction_network_shared->set_active(shared);
-  m_connection_toggleaction_network_shared.unblock();
  
   //Do not allow impossible changes:
   const Document::HostingMode hosting_mode = document->get_hosting_mode();
@@ -1215,6 +1210,8 @@ void App_Glom::update_network_shared_ui()
   {
     m_toggleaction_network_shared->set_sensitive(false);
   }
+
+  m_connection_toggleaction_network_shared.unblock();
 }
 
 void App_Glom::update_userlevel_ui()
diff --git a/glom/base_db.cc b/glom/base_db.cc
index 415083c..c7709a5 100644
--- a/glom/base_db.cc
+++ b/glom/base_db.cc
@@ -1093,6 +1093,8 @@ bool Base_DB::add_standard_groups()
     type_vec_strings::const_iterator iterFind = std::find(vecGroups.begin(), vecGroups.end(), devgroup);
     if(iterFind == vecGroups.end())
     {
+      //The "SUPERUSER" here has no effect because SUPERUSER is not "inherited" to member users.
+      //But let's keep it to make the purpose of this group obvious.
       bool test = query_execute("CREATE GROUP \"" GLOM_STANDARD_GROUP_NAME_DEVELOPER "\" WITH SUPERUSER");
       if(!test)
       {
@@ -3281,4 +3283,135 @@ void Base_DB::update_gda_metastore_for_table(const Glib::ustring& table_name) co
   std::cout << "DEBUG: Base_DB::update_gda_metastore_for_table(): ... Finished calling Gda::Connection::update_meta_store_table()" << std::endl;
 }
 
+bool Base_DB::add_user(const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group)
+{
+  if(user.empty() || password.empty() || group.empty())
+    return false;
+
+  //Create the user:
+  //Note that ' around the user fails, so we use ".
+  Glib::ustring strQuery = "CREATE USER \"" + user + "\" PASSWORD '" + password + "'" ; //TODO: Escape the password.
+  if(group == GLOM_STANDARD_GROUP_NAME_DEVELOPER)
+    strQuery += " SUPERUSER CREATEDB CREATEROLE"; //Because SUPERUSER is not "inherited" from groups to members.
+  
+
+  //Glib::ustring strQuery = "CREATE USER \"" + user + "\"";
+  //if(group == GLOM_STANDARD_GROUP_NAME_DEVELOPER)
+  //  strQuery += " WITH SUPERUSER"; //Because SUPERUSER is not "inherited" from groups to members.
+  //strQuery +=  " PASSWORD '" + password + "'" ; //TODO: Escape the password.
+
+
+  bool test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::add_user(): CREATE USER failed." << std::endl;
+    return false;
+  }
+
+  //Add it to the group:
+  strQuery = "ALTER GROUP \"" + group + "\" ADD USER \"" + user + "\"";
+  test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::add_user(): ALTER GROUP failed." << std::endl;
+    return false;
+  }
+
+  //Remove any user rights, so that all rights come from the user's presence in the group:
+  Document* document = get_document();
+  if(!document)
+    return true;
+
+  Document::type_listTableInfo table_list = document->get_tables();
+
+  for(Document::type_listTableInfo::const_iterator iter = table_list.begin(); iter != table_list.end(); ++iter)
+  {
+    const Glib::ustring strQuery = "REVOKE ALL PRIVILEGES ON \"" + (*iter)->get_name() + "\" FROM \"" + user + "\"";
+    const bool test = query_execute(strQuery);
+    if(!test)
+      std::cerr << "Base_DB::add_user(): REVOKE failed." << std::endl;
+  }
+
+  return true;
+}
+
+
+bool Base_DB::remove_user(const Glib::ustring& user)
+{
+  if(user.empty())
+    return false;
+
+  const Glib::ustring strQuery = "DROP USER \"" + user + "\"";
+  const bool test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::remove_user(): DROP USER failed" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool Base_DB::remove_user_from_group(const Glib::ustring& user, const Glib::ustring& group)
+{
+  if(user.empty() || group.empty())
+    return false;
+
+  const Glib::ustring strQuery = "ALTER GROUP \"" + group + "\" DROP USER \"" + user + "\"";
+  const bool test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::remove_user_from_group(): ALTER GROUP failed." << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool Base_DB::set_database_owner_user(const Glib::ustring& user)
+{
+  if(user.empty())
+    return false;
+
+  ConnectionPool* connectionpool = ConnectionPool::get_instance();
+  const Glib::ustring database_name = connectionpool->get_database();
+  if(database_name.empty())
+    return false;
+
+  const Glib::ustring strQuery = "ALTER DATABASE \"" + database_name + "\" OWNER TO \"" + user + "\"";
+  const bool test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::set_database_owner_user(): ALTER DATABSE failed." << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+
+bool Base_DB::disable_user(const Glib::ustring& user)
+{
+  if(user.empty())
+    return false;
+
+  type_vec_strings vecGroups = Privs::get_groups_of_user(user);
+  for(type_vec_strings::const_iterator iter = vecGroups.begin(); iter != vecGroups.end(); ++iter)
+  {
+    const Glib::ustring group = *iter;
+    remove_user_from_group(user, group);
+  }
+
+  const Glib::ustring strQuery = "ALTER ROLE \"" + user + "\" NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE";
+  const bool test = query_execute(strQuery);
+  if(!test)
+  {
+    std::cerr << "Base_DB::remove_user(): DROP USER failed" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+
 } //namespace Glom
diff --git a/glom/base_db.h b/glom/base_db.h
index 7d6ddf7..ec8c061 100644
--- a/glom/base_db.h
+++ b/glom/base_db.h
@@ -376,6 +376,28 @@ protected:
 
   static type_vec_strings util_vecStrings_from_Fields(const type_vec_fields& fields);
 
+  /** Add a @a user to the database, with the specified @a password, in the specified @a group.
+   * @result true if the addition succeeded.
+   */
+  bool add_user(const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group);
+
+  /** Remove the @a user from the database.
+   * @result true if the removal succeeded.
+   */
+  bool remove_user(const Glib::ustring& user);
+
+  bool remove_user_from_group(const Glib::ustring& user, const Glib::ustring& group);
+
+  bool set_database_owner_user(const Glib::ustring& user);
+
+  /** Revoke any login rights from the user and remove it from any groups.
+   * This is a workaround for these problems:
+   * 1. We can only specify a superuser _user_, not a role, to initdb (because it needs a password, but groups have no password),
+   *    so that user is then the owner of various objects.
+   * 2. Even when changing the owner of these objects we still get this error "cannot drop role glom_default_developer_user because it is required by the database system"
+   */
+  bool disable_user(const Glib::ustring& user);
+
 
   static void handle_error(const Glib::Exception& ex);
   static void handle_error(const std::exception& ex); //TODO_port: This is probably useless now.
diff --git a/glom/dialog_connection.cc b/glom/dialog_connection.cc
index 91d4fbc..941ca9d 100644
--- a/glom/dialog_connection.cc
+++ b/glom/dialog_connection.cc
@@ -38,30 +38,29 @@ Dialog_Connection::Dialog_Connection(BaseObjectType* cobject, const Glib::RefPtr
   m_entry_host(0),
   m_entry_user(0),
   m_entry_password(0),
-  m_label_database(0)
+  m_label_database(0),
+  m_label_note(0)
 {
   builder->get_widget("entry_host", m_entry_host);
   builder->get_widget("entry_user", m_entry_user);
   builder->get_widget("entry_password", m_entry_password);
   builder->get_widget("label_database", m_label_database);
+  builder->get_widget("connection_note", m_label_note);
 
 #ifdef GLOM_ENABLE_MAEMO
   // Make the bold title the window title (which cannot be empty in maemo
   // because it displays <Untitled window> instead). This also helps to
   // make the dialog smaller in height, so we save a bit screen space required
   // by the onscreen keyboard.
-  Gtk::Label* title;
-  Gtk::Label* note;
-
+  Gtk::Label* title = 0;
   builder->get_widget("connection_title", title);
-  builder->get_widget("connection_note", note);
 
   set_title(title->get_text());
   title->hide();
 
   // Without size request, this label enlarges the dialog significantly,
   // and the text is still truncated.
-  note->set_size_request(400, -1);
+  m_label_note->set_size_request(400, -1);
 #endif
 }
 
@@ -98,7 +97,7 @@ sharedptr<SharedConnection> Dialog_Connection::connect_to_server_with_connection
       {
         ConnectionPool::Backend* backend = connection_pool->get_backend();
         ConnectionPoolBackends::PostgresCentralHosted* central = dynamic_cast<ConnectionPoolBackends::PostgresCentralHosted*>(backend);
-        g_assert(central != NULL);
+        g_assert(central);
 
         central->set_host(m_entry_host->get_text());
       }
@@ -247,4 +246,9 @@ void Dialog_Connection::get_username_and_password(Glib::ustring& username, Glib:
   password = m_entry_password->get_text();
 }
 
+void Dialog_Connection::set_confirm_existing_user_note()
+{
+  m_label_note->set_text(_(""));
+}
+
 } //namespace Glom
diff --git a/glom/dialog_connection.h b/glom/dialog_connection.h
index 8ecd1e1..1c07b21 100644
--- a/glom/dialog_connection.h
+++ b/glom/dialog_connection.h
@@ -48,6 +48,12 @@ public:
   ///Disable irrelevant fields:
   void set_connect_to_browsed();
 
+  /** Change the main message text of the dialog, so we can use the dialog 
+   * to confirm that the user knows a password before disconnecting, when 
+   * switching to network sharing.
+   */
+  void set_confirm_existing_user_note();
+
   void set_username(const Glib::ustring& username);
   void set_password(const Glib::ustring& password);
   void get_username_and_password(Glib::ustring& user, Glib::ustring& password) const;
@@ -66,6 +72,7 @@ private:
   Gtk::Entry* m_entry_user;
   Gtk::Entry* m_entry_password;
   Gtk::Label* m_label_database;
+  Gtk::Label* m_label_note;
   Glib::ustring m_database_name;
 };
 
diff --git a/glom/frame_glom.cc b/glom/frame_glom.cc
index d26ca72..6985aec 100644
--- a/glom/frame_glom.cc
+++ b/glom/frame_glom.cc
@@ -836,11 +836,24 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
 {
   if(!action)
   {
-    std::cerr << "rame_Glom::on_menu_file_toggle_share(): action was null." << std::endl;
+    std::cerr << "Frame_Glom::on_menu_file_toggle_share(): action was null." << std::endl;
   }
 
+  //Prevent this change if not in developer mode,
+  //though the menu item should be disabled then anyway.
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document || document->get_userlevel() != AppState::USERLEVEL_DEVELOPER)
+    return;
 
   bool shared = action->get_active(); //Whether it should be shared.
+  if(shared == document->get_network_shared())
+  {
+    //Do nothing, because things are already as requested.
+    //This is probably just an extra signal emitted when we set the toggle in the UI.
+    //So we avoid the endless loop:
+    return;
+  }
+
   bool change = true;
 
   //Ask user for confirmation:
@@ -854,26 +867,126 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
     dialog.add_button(_("_Share"), Gtk::RESPONSE_OK);
 
     const int response = dialog.run();
+    dialog.hide();
     if(response == Gtk::RESPONSE_OK)
+    {
       shared = true;
+
+      //Ask for a user/password if none is set:
+      const bool real_user_exists = Privs::get_developer_user_exists_with_password();
+      if(!real_user_exists)
+      {
+        //Ask for an initial user:
+        Glib::ustring user, password;
+        const bool initial_password_provided = connection_request_initial_password(user, password);
+        bool added = false;
+        if(initial_password_provided)
+          added = add_user(user, password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
+        
+        if(initial_password_provided && added)
+        {
+          //Use the new user/password from now on:
+          ConnectionPool* connectionpool = ConnectionPool::get_instance();      
+          connectionpool->set_user(user);
+          connectionpool->set_password(password);
+        }
+        else
+        {
+          shared = false;
+          change = false;
+        }
+      }
+      else
+      {
+        //Ask for the password of a developer user, to 
+        //a) Check that the user knows it, so he won't lose access.
+        //b) Reconnect as that user so we can remove the default user.
+        //TODO: Check that this user is a developer.
+        bool database_not_found = false; //Ignored;
+        const bool dev_password_known = connection_request_password_and_attempt(database_not_found, "" ,"", true /* alternative text */);
+        if(!dev_password_known)
+        {
+          shared = false;
+          change = false;
+        }
+      }
+
+      if(change) //If nothing has gone wrong so far.
+      {
+        //Remove the default no-password user, because that would be a security hole:
+        //We do this after adding/using the non-default user, because we can't 
+        //remove a currently-used user.
+        const bool default_user_exists = Privs::get_default_developer_user_exists();
+        if(default_user_exists)
+        {
+          //Force a reconnection with the new password:
+          //ConnectionPool* connectionpool = ConnectionPool::get_instance();      
+
+          //Remove it, after stopping it from being the database owner: 
+          bool disabled = true;
+          Glib::ustring default_password;
+          const Glib::ustring default_user = Privs::get_default_developer_user_name(default_password);
+
+          ConnectionPool* connectionpool = ConnectionPool::get_instance();
+          const bool reowned = set_database_owner_user(connectionpool->get_user());
+          bool removed = false;
+          if(reowned)
+            removed = remove_user(default_user);
+
+          if(!removed)
+          {
+            //This is a workaround.
+            //Try to revoke it instead.
+            //TODO: Discover how to make remove_user() succeed.
+            disabled = disable_user(default_user);
+          }
+
+          if(!reowned || !(removed || disabled))
+          {
+            std::cerr << "Frame_Glom::on_menu_file_toggle_share(): Failed to reown and remove/revoke default user." << std::endl;
+            shared = false;
+            change = false;
+          }
+        }
+      }
+    }
     else
     {
       shared = false;
       change = false;
     }
   }
-  else
+  else //not shared:
   {
     //TODO: Warn about connected users if possible.
     Gtk::MessageDialog dialog(Utils::bold_message(_("Stop Sharing On Network")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
     dialog.set_secondary_text(_("Are you sure that you wish to prevent other users on the network from using this database?"));
     dialog.set_transient_for(*get_app_window());
     dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
-    dialog.add_button(_("_Share"), Gtk::RESPONSE_OK);
+    dialog.add_button(_("_Stop Sharing"), Gtk::RESPONSE_OK);
 
     const int response = dialog.run();
+    dialog.hide();
     if(response == Gtk::RESPONSE_OK)
+    {
       shared = false;
+
+      //Make sure the default no-password user exists:
+      const bool default_user_exists = Privs::get_default_developer_user_exists();
+      if(!default_user_exists)
+      {
+        //Add it:
+        Glib::ustring default_password;
+        const Glib::ustring default_user = Privs::get_default_developer_user_name(default_password);
+
+        const bool added = add_user(default_user, default_password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
+        if(!added)
+        {
+           shared = true;
+           change = false;
+        }
+      }
+    }
     else
     {
       shared = true;
@@ -881,10 +994,8 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
     }
   }
 
-
-  Document* document = get_document();
   if(document)
-   document->set_network_shared(shared);
+    document->set_network_shared(shared);
 
 
   //Stop the self-hosted database server,
@@ -922,7 +1033,9 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
   //Update the UI:
   App_Glom* pApp = dynamic_cast<App_Glom*>(get_app_window());
   if(pApp)
+  {
     pApp->update_network_shared_ui();
+  }
 }
 
 void Frame_Glom::on_menu_file_print()
@@ -1852,6 +1965,85 @@ bool Frame_Glom::handle_connection_initialize_errors(ConnectionPool::InitErrors
   return false;
 }
 
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+bool Frame_Glom::connection_request_initial_password(Glib::ustring& user, Glib::ustring& password)
+{
+  //Intialze output parameters:
+  user = Glib::ustring();
+  password = Glib::ustring();
+
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document)
+    return false;
+
+  //This is only useful for self-hosted postgres:
+  if(document->get_hosting_mode() != Document::HOSTING_MODE_POSTGRES_SELF)
+    return false;
+
+  //Ask for a new username and password to specify when creating a new self-hosted database.
+  Dialog_NewSelfHostedConnection* dialog = 0;
+  Glib::RefPtr<Gtk::Builder> refXml;
+
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+  {
+    refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection");
+  }
+  catch(const Gtk::BuilderError& ex)
+  {
+    std::cerr << ex.what() << std::endl;
+    return false;
+  }
+#else
+  std::auto_ptr<Gtk::BuilderError> error;
+  refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection", error);
+  if(error.get())
+  {
+    std::cerr << error->what() << std::endl;
+    return false;
+  }
+#endif //GLIBMM_EXCEPTIONS_ENABLED
+
+  refXml->get_widget_derived("dialog_new_self_hosted_connection", dialog);
+  if(!dialog)
+    return false;
+
+  add_view(dialog);
+
+
+  int response = Gtk::RESPONSE_OK;
+  bool keep_trying = true;
+  while(keep_trying)
+  {
+    response = Utils::dialog_run_with_help(dialog, "dialog_new_self_hosted_connection");
+
+    //Check the password is acceptable:
+    if(response == Gtk::RESPONSE_OK)
+    {
+      const bool password_ok = dialog->check_password();
+      if(password_ok)
+      {
+        user = dialog->get_user();
+        password = dialog->get_password();
+
+        keep_trying = false; //Everything is OK.
+      }
+    }
+    else
+    {
+      keep_trying = false; //The user cancelled.
+    }
+
+    dialog->hide();
+  }
+
+  remove_view(dialog);
+  delete dialog;
+  dialog = 0;
+
+  return (response == Gtk::RESPONSE_OK);
+}
+
 bool Frame_Glom::connection_request_password_and_choose_new_database_name()
 {
   Document* document = dynamic_cast<Document*>(get_document());
@@ -1867,102 +2059,54 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
     add_view(m_pDialogConnection); //Also a composite view.
   }
 
-  //Ask either for the existing username and password to use an existing database server,
-  //or ask for a new username and password to specify when creating a new self-hosted database.
   switch(document->get_hosting_mode())
   {
-#ifdef GLOM_ENABLE_POSTGRESQL
-  case Document::HOSTING_MODE_POSTGRES_SELF:
+    case Document::HOSTING_MODE_POSTGRES_SELF:
     {
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-      Dialog_NewSelfHostedConnection* dialog = 0;
-      Glib::RefPtr<Gtk::Builder> refXml;
+      Glib::ustring user, password;
 
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-      try
+      if(document->get_network_shared()) //Usually not the case when creating new documents.
       {
-        refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection");
-      }
-      catch(const Gtk::BuilderError& ex)
-      {
-        std::cerr << ex.what() << std::endl;
-        return false;
-      }
-#else
-      std::auto_ptr<Gtk::BuilderError> error;
-      refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection", error);
-      if(error.get())
-      {
-        std::cerr << error->what() << std::endl;
-        return false;
+        const bool test = connection_request_initial_password(user, password);
+        if(!test)
+          return false;
       }
-#endif //GLIBMM_EXCEPTIONS_ENABLED
-
-      refXml->get_widget_derived("dialog_new_self_hosted_connection", dialog);
-      if(!dialog) return false;
-
-      add_view(dialog);
-
-
-      int response = Gtk::RESPONSE_OK;
-      bool keep_trying = true;
-      while(keep_trying)
+      else
       {
-        response = Utils::dialog_run_with_help(dialog, "dialog_new_self_hosted_connection");
-
-        //Check the password is acceptable:
-        if(response == Gtk::RESPONSE_OK)
-        {
-          const bool password_ok = dialog->check_password();
-          if(password_ok)
-          {
-            keep_trying = false; //Everything is OK.
-          }
-        }
-        else
-          keep_trying = false; //The user cancelled.
-
+        //Use the default user because we are not network shared:
+        user = Privs::get_default_developer_user_name(password);
       }
-
-      dialog->hide();
  
       // Create the requested self-hosting database:
-      if(response == Gtk::RESPONSE_OK)
-      {
-        //Set the connection details in the ConnectionPool singleton.
-        //The ConnectionPool will now use these every time it tries to connect.
-        connection_pool->set_user(dialog->get_user());
-        connection_pool->set_password(dialog->get_password());
       
-        const bool initialized = handle_connection_initialize_errors( connection_pool->initialize(
-          sigc::mem_fun(*this, &Frame_Glom::on_connection_initialize_progress) ) );
-
-        if(m_dialog_progess_connection_initialize)
-        {
-          delete m_dialog_progess_connection_initialize;
-          m_dialog_progess_connection_initialize = 0;
-        }
-
-        if(!initialized)
-          return false;
+      //Set the connection details in the ConnectionPool singleton.
+      //The ConnectionPool will now use these every time it tries to connect.
+      connection_pool->set_user(user);
+      connection_pool->set_password(password);
+      
+      const bool initialized = handle_connection_initialize_errors( connection_pool->initialize(
+        sigc::mem_fun(*this, &Frame_Glom::on_connection_initialize_progress) ) );
 
-        //Put the details into m_pDialogConnection too, because that's what we use to connect.
-        //This is a bit of a hack:
-        m_pDialogConnection->set_self_hosted_user_and_password(connection_pool->get_user(), connection_pool->get_password());
+      if(m_dialog_progess_connection_initialize)
+      {
+        delete m_dialog_progess_connection_initialize;
+        m_dialog_progess_connection_initialize = 0;
       }
 
-      remove_view(dialog);
+      if(!initialized)
+        return false;
 
-      //std::cout << "DEBUG: after connection_pool->initialize(). The database cluster should now exist." << std::endl;
+      //Put the details into m_pDialogConnection too, because that's what we use to connect.
+      //This is a bit of a hack:
+      m_pDialogConnection->set_self_hosted_user_and_password(connection_pool->get_user(), connection_pool->get_password());
 
-#else
-      // Self-hosted postgres not supported in client only mode
-      g_assert_not_reached();
-#endif // !GLOM_ENABLE_CLIENT_ONLY
+      //std::cout << "DEBUG: after connection_pool->initialize(). The database cluster should now exist." << std::endl;
     }
 
     break;
-  case Document::HOSTING_MODE_POSTGRES_CENTRAL:
+
+#ifdef GLOM_ENABLE_POSTGRES
+    case Document::HOSTING_MODE_POSTGRES_CENTRAL:
     {
       //Ask for connection details:
       m_pDialogConnection->load_from_document(); //Get good defaults.
@@ -2055,7 +2199,7 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
     if(error.get())
     {
       const ExceptionConnection& ex = *error;
-#endif
+#endif //GLIBMM_EXCEPTIONS_ENABLED
       //g_warning("Frame_Glom::connection_request_password_and_choose_new_database_name(): caught exception.");
 
       if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
@@ -2084,7 +2228,6 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
           document->set_connection_server(central->get_host());
         }
 
-        #ifndef GLOM_ENABLE_CLIENT_ONLY
         // Remember port if the document is self-hosted, so that remote
         // connections to the database (usinc browse network) know what port to use.
         // TODO: There is already similar code in
@@ -2099,7 +2242,6 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
 
           document->set_connection_port(self->get_port());
         }
-        #endif //GLOM_ENABLE_CLIENT_ONLY
 
         #endif //GLOM_ENABLE_POSTGRESQL
 
@@ -2112,6 +2254,7 @@ bool Frame_Glom::connection_request_password_and_choose_new_database_name()
   
   return false;
 }
+#endif //GLOM_ENABLE_CLIENT_ONLY
 
 void Frame_Glom::cleanup_connection()
 {
@@ -2125,31 +2268,15 @@ void Frame_Glom::cleanup_connection()
   }
 }
 
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring known_username, const Glib::ustring& known_password)
-#else
-bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring known_username, const Glib::ustring& known_password, std::auto_ptr<ExceptionConnection>& error)
-#endif
+bool Frame_Glom::connection_request_password_and_attempt(bool& database_not_found, const Glib::ustring known_username, const Glib::ustring& known_password, bool confirm_known_user)
 {
+  //Initialize output parameter:
+  database_not_found = false;
+
   Document* document = dynamic_cast<Document*>(get_document());
   if(!document)
     return false;
 
-  if(!m_pDialogConnection)
-  {
-    Utils::get_glade_widget_derived_with_warning("dialog_connection", m_pDialogConnection);
-    add_view(m_pDialogConnection); //Also a composite view.
-  }
-  
-  m_pDialogConnection->load_from_document(); //Get good defaults.
-  m_pDialogConnection->set_transient_for(*get_app_window());
-
-  if(!known_username.empty())
-    m_pDialogConnection->set_username(known_username);
-
-  if(!known_password.empty())
-    m_pDialogConnection->set_password(known_password);
-
 
   //Start a self-hosted server if necessary:
   ConnectionPool* connection_pool = ConnectionPool::get_instance();
@@ -2163,6 +2290,41 @@ bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring kno
     m_dialog_progess_connection_startup = 0;
   }
 
+  //Only ask for the password if we are shared on the network.
+  //Otherwise, no password question is necessary, due to how our self-hosted database server is configured.
+  if(document->get_network_shared()
+    && (document->get_hosting_mode() != Document::HOSTING_MODE_SQLITE) ) //TODO: The sqlite check may be unnecessary.
+  {
+    //We recreate the dialog each time to make sure it is clean of any changes:
+    if(m_pDialogConnection)
+    {
+      delete m_pDialogConnection;
+      m_pDialogConnection = 0;
+    }
+
+    Utils::get_glade_widget_derived_with_warning("dialog_connection", m_pDialogConnection);
+    add_view(m_pDialogConnection); //Also a composite view.
+  
+    m_pDialogConnection->load_from_document(); //Get good defaults.
+    m_pDialogConnection->set_transient_for(*get_app_window());
+
+    //Show alternative text if necessary:
+    if(confirm_known_user)
+      m_pDialogConnection->set_confirm_existing_user_note();
+
+    if(!known_username.empty())
+      m_pDialogConnection->set_username(known_username);
+
+    if(!known_password.empty())
+      m_pDialogConnection->set_password(known_password);
+  }
+  else if(m_pDialogConnection)
+  {
+    //Later, if m_pDialogConnection is null then we assume we should use the known user/password:
+    delete m_pDialogConnection;
+    m_pDialogConnection = 0;
+  }
+
 
   //Ask for connection details: 
   while(true) //Loop until a return
@@ -2170,19 +2332,10 @@ bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring kno
     //Only show the dialog if we don't know the correct username/password yet:
     int response = Gtk::RESPONSE_OK;
 
-    // Don't ask for user/password for sqlite databases, since sqlite does
-    // not support authentication. I'd prefer to get that information from
-    // libgda, but gda_connection_supports_feature() requires a GdaConnection
-    // which we don't have at this point.
-#ifdef GLOM_ENABLE_SQLITE
-    if(document->get_hosting_mode() != Document::HOSTING_MODE_SQLITE)
-#endif
+    if(m_pDialogConnection && known_username.empty() && known_password.empty())
     {
-      if(known_username.empty() && known_password.empty())
-      {
-        response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
-        m_pDialogConnection->hide();
-      }
+      response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
+      m_pDialogConnection->hide();
     }
 
     //Try to use the entered username/password:
@@ -2192,8 +2345,30 @@ bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring kno
       try
       {
         //TODO: Remove any previous database setting?
-        sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
-        return true; //Succeeded, because no exception was thrown.
+        if(m_pDialogConnection)
+        {
+          sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
+          return true; //Succeeded, because no exception was thrown.
+        }
+        else
+        {
+          //Use the known password:
+          ConnectionPool* connectionpool = ConnectionPool::get_instance();
+          connectionpool->set_user(known_username);
+          connectionpool->set_password(known_password);
+    
+          #ifdef GLIBMM_EXCEPTIONS_ENABLED
+          Base_DB::connect_to_server(get_app_window());
+          return true; //Succeeded, because no exception was thrown.
+          #else
+          std::auto_ptr<ExceptionConnection> error;
+          const bool connected = Base_DB::connect_to_server(get_app_window(), error);
+          if(!connected || error)
+            return false;
+          else
+            return true;
+          #endif
+        }
       }
       catch(const ExceptionConnection& ex)
       {
@@ -2208,25 +2383,26 @@ bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring kno
 #endif
         g_warning("Frame_Glom::connection_request_password_and_attempt(): caught exception.");
 
-        if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
+        if(m_pDialogConnection && ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
         {
           //Warn the user, and let him try again:
           Utils::show_ok_dialog(_("Connection Failed"), _("Glom could not connect to the database server. Maybe you entered an incorrect user name or password, or maybe the postgres database server is not running."), *(get_app_window()), Gtk::MESSAGE_ERROR); //TODO: Add help button.
-
+         
           //The while() loop will run again, showing the username/password dialog again.
         }
-        else
+        else if (ex.get_failure_type() == ExceptionConnection::FAILURE_NO_DATABASE)
         {
-          g_warning("Frame_Glom::connection_request_password_and_attempt(): rethrowing exception.");
-
           cleanup_connection();
 
-          //The connection to the server is OK, but the specified database does not exist:
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-          throw ex; //Pass it on for the caller to handle.
-#else
-          error = local_error; //Pass it on for the caller to handle.
-#endif
+          //The connection to the server might be OK, but the specified database does not exist:
+          //Or the connection failed when trying without a password.
+          database_not_found = true; //Tell the caller about this error.
+          return false;
+        }
+        else
+        {
+          std::cerr << "Frame_Glom::connection_request_password_and_attempt(): Unexpected exception: " << ex.what() << std::endl;
+          cleanup_connection();
           return false;
         }
       }
diff --git a/glom/frame_glom.h b/glom/frame_glom.h
index 3d1eecc..b080988 100644
--- a/glom/frame_glom.h
+++ b/glom/frame_glom.h
@@ -144,17 +144,20 @@ public:
 
   static void show_ok_dialog(const Glib::ustring& title, const Glib::ustring& message, Gtk::Window& parent, Gtk::MessageType message_type = Gtk::MESSAGE_INFO);
 
-  //Show the dialog to request the password, and check whether it works.
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  bool connection_request_password_and_attempt(const Glib::ustring known_username = Glib::ustring(), const Glib::ustring& known_password = Glib::ustring());
-#else
-  bool connection_request_password_and_attempt(const Glib::ustring known_username = Glib::ustring(), const Glib::ustring& known_password = Glib::ustring(), std::auto_ptr<ExceptionConnection>& error);
-#endif
+  /** Show the dialog to request the password, and check whether it works.
+   *
+   * @param database_not_found true if the connection failed only because the database was not found on the server.
+   * @param known_username The username if known. Otherwise, the user will be asked via a dialog.
+   * @param known_password The password if known. Otherwise, the user will be asked via a dialog.
+   * @param confirm_existing_user If true then an alternative message text will be shown.
+   * @result true if the connection succeeded and the database was found on the server.
+   */
+  bool connection_request_password_and_attempt(bool& database_not_found, const Glib::ustring known_username = Glib::ustring(), const Glib::ustring& known_password = Glib::ustring(), bool confirm_existing_user = false);
 
+#ifndef GLOM_ENABLE_CLIENT_ONLY
   //Show the dialog to request the password, and choose an unused database name.
   bool connection_request_password_and_choose_new_database_name();
 
-#ifndef GLOM_ENABLE_CLIENT_ONLY
   ///Create the database for new documents, showing the Connection dialog
   bool create_database(const Glib::ustring& database_name, const Glib::ustring& title);
   void show_layout_toolbar(bool show = true);
@@ -173,6 +176,8 @@ public:
 
 protected:
 
+  
+
   //virtual void set_document(Document* pDocument); //override
 
   /** Show the table, possibly selecting a particular record, possibly showing that in the details tab. This allows table_name to be empty in which case no
@@ -195,9 +200,13 @@ protected:
   void show_no_table();
 
   void show_table_title();
+
 #ifndef GLOM_ENABLE_CLIENT_ONLY
+  bool connection_request_initial_password(Glib::ustring& user, Glib::ustring& password);
+
   void update_table_in_document_from_database();
 #endif // !GLOM_ENABLE_CLIENT_ONLY
+
   virtual void set_mode_widget(Gtk::Widget& widget); //e.g. show the design mode notebook.
   virtual bool set_mode(enumModes mode); //bool indicates that there was a change.
 
diff --git a/glom/glom_developer.glade b/glom/glom_developer.glade
index 3f22c5c..673fd2c 100644
--- a/glom/glom_developer.glade
+++ b/glom/glom_developer.glade
@@ -2151,7 +2151,7 @@
                     <property name="yalign">0</property>
                     <property name="label" translatable="yes">&lt;span weight="bold" size="larger"&gt;First User&lt;/span&gt;
 
-Please enter the initial connection details for your new database. You may add additional users later. Remember to keep this password secret because it allows access to your data from other computers on the network.</property>
+Please enter the initial connection details for your database. You may add additional users later. Remember to keep this password secret because it allows access to your data from other computers on the network.</property>
                     <property name="use_markup">True</property>
                     <property name="wrap">True</property>
                     <property name="selectable">True</property>
diff --git a/glom/glom_privs.cc b/glom/glom_privs.cc
index 1bc21cf..eb9918e 100644
--- a/glom/glom_privs.cc
+++ b/glom/glom_privs.cc
@@ -49,6 +49,44 @@ Privs::type_vec_strings Privs::get_database_groups()
   return result;
 }
 
+bool Privs::get_default_developer_user_exists()
+{
+  Glib::ustring default_password;
+  const Glib::ustring default_user = get_default_developer_user_name(default_password);
+
+  const type_vec_strings users = get_database_users();
+  type_vec_strings::const_iterator iterFind = std::find(users.begin(), users.end(), default_user);
+  if(iterFind != users.end())
+    return true; //We assume that the password is what it should be and that it has developer rights.
+
+  return false; //The default user is not there.
+}
+
+bool Privs::get_developer_user_exists_with_password()
+{
+  Glib::ustring default_password;
+  const Glib::ustring default_user = get_default_developer_user_name(default_password);
+
+  const type_vec_strings users = get_database_users();
+  for(type_vec_strings::const_iterator iter = users.begin(); iter != users.end(); iter++)
+  {
+    const Glib::ustring user = *iter;
+    if(user == default_user)
+      continue;
+
+    if(get_user_is_in_group(user, GLOM_STANDARD_GROUP_NAME_DEVELOPER))
+      return true;
+  }
+
+  return false;
+}
+
+Glib::ustring Privs::get_default_developer_user_name(Glib::ustring& password)
+{
+  password = "glom_default_developer_password";
+  return "glom_default_developer_user";
+}
+
 Privs::type_vec_strings Privs::get_database_users(const Glib::ustring& group_name)
 {
   BusyCursor cursor(App_Glom::get_application());
diff --git a/glom/glom_privs.h b/glom/glom_privs.h
index 34f1465..d7915c0 100644
--- a/glom/glom_privs.h
+++ b/glom/glom_privs.h
@@ -34,14 +34,46 @@ class Privs : public GlomPostgres
 {
 public:
 
+  /** Get the groups with access to the database.
+   */
   static type_vec_strings get_database_groups();
+
+  /** Get users with access to the database.
+   * @param group_name Get only users in this group.
+   */
   static type_vec_strings get_database_users(const Glib::ustring& group_name = Glib::ustring());
+
+
+  /** Discover whether there is already a user with a real password,
+   * instead of just a default glom user and default password,
+   * The user should be forced to choose a user/password when network sharing is active.
+   */
+  static bool get_developer_user_exists_with_password();
+
+  /** Discover whether the default developer user exists (which has a known password).
+   * The user should be forced to choose a user/password when network sharing is active,
+   * and this default user should no longer exist in that case.
+   */
+  static bool get_default_developer_user_exists();
+
+  /** Get the standard username and password used for the no-password user,
+   * which should only be used when network sharing is not active.
+   */
+  static Glib::ustring get_default_developer_user_name(Glib::ustring& password);
+
   static Privileges get_table_privileges(const Glib::ustring& group_name, const Glib::ustring& table_name);
   static void set_table_privileges(const Glib::ustring& group_name, const Glib::ustring& table_name, const Privileges& privs, bool developer_privs = false);
+
   static Glib::ustring get_user_visible_group_name(const Glib::ustring& group_name);
 
+  /** Get the groups that the user is a member of.
+   */
   static type_vec_strings get_groups_of_user(const Glib::ustring& user);
+
   static bool get_user_is_in_group(const Glib::ustring& user, const Glib::ustring& group);
+
+  /** Get the privileges (access rights) that the current user has.
+   */
   static Privileges get_current_privs(const Glib::ustring& table_name);
 
 private:
diff --git a/glom/libglom/connectionpool.cc b/glom/libglom/connectionpool.cc
index 1a69f29..480805d 100644
--- a/glom/libglom/connectionpool.cc
+++ b/glom/libglom/connectionpool.cc
@@ -380,8 +380,18 @@ sharedptr<SharedConnection> ConnectionPool::connect(std::auto_ptr<ExceptionConne
         std::cout << "DEBUG: Calling update_meta_store_data_types() ..." << std::endl;
         m_refGdaConnection->update_meta_store_data_types();
         std::cout << "DEBUG: ... update_meta_store_data_types() has finished." << std::endl;
+
         std::cout << "DEBUG: Calling update_meta_store_table_names() ..." << std::endl;
-        m_refGdaConnection->update_meta_store_table_names();
+        try
+        {
+          //update_meta_store_table_names() has been known to throw an exception.
+          //Glom is mostly unusable when it fails, but that's still better than a crash.
+          m_refGdaConnection->update_meta_store_table_names();
+        }
+        catch(const Glib::Error& ex)
+        {
+          std::cerr << "update_meta_store_table_names() failed: " << ex.what() << std::endl;
+        }
         std::cout << "DEBUG: ... update_meta_store_table_names() has finished." << std::endl;
 
         // Connection succeeded
@@ -449,7 +459,7 @@ void ConnectionPool::set_user(const Glib::ustring& value)
   m_user = value;
 
   //Make sure that connect() makes a new connection:
-  connection_cached.clear();
+  invalidate_connection();
 }
 
 void ConnectionPool::set_password(const Glib::ustring& value)
@@ -457,7 +467,7 @@ void ConnectionPool::set_password(const Glib::ustring& value)
   m_password = value;
   
   //Make sure that connect() makes a new connection:
-  connection_cached.clear();
+  invalidate_connection();
 }
 
 void ConnectionPool::set_database(const Glib::ustring& value)
@@ -465,7 +475,7 @@ void ConnectionPool::set_database(const Glib::ustring& value)
   m_database = value;
 
   //Make sure that connect() makes a new connection:
-  connection_cached.clear();
+  invalidate_connection();
 }
 
 Glib::ustring ConnectionPool::get_user() const
diff --git a/glom/libglom/connectionpool_backends/postgres.cc b/glom/libglom/connectionpool_backends/postgres.cc
index ea1fc8d..619ecf3 100644
--- a/glom/libglom/connectionpool_backends/postgres.cc
+++ b/glom/libglom/connectionpool_backends/postgres.cc
@@ -32,7 +32,10 @@ namespace
 
 static Glib::ustring create_auth_string(const Glib::ustring& username, const Glib::ustring& password)
 {
-  return "USERNAME=" + username + ";PASSWORD=" + password;
+  if(username.empty() and password.empty())
+    return Glib::ustring();
+  else
+    return "USERNAME=" + username + ";PASSWORD=" + password; //TODO: How should we quote and escape these?
 }
 
 } //anonymous namespace
@@ -71,10 +74,16 @@ Glib::RefPtr<Gnome::Gda::Connection> Postgres::attempt_connect(const Glib::ustri
   Glib::RefPtr<Gnome::Gda::Connection> connection;
   Glib::RefPtr<Gnome::Gda::DataModel> data_model;
 
+  const Glib::ustring auth_string = create_auth_string(username, password);   
+ 
+#ifdef GLOM_CONNECTION_DEBUG
+  std::cout << "DEBUG: ConnectionPoolBackends::Postgres::attempt_connect(): cnc_string=" << cnc_string << std::endl;
+  std::cout << "  DEBUG: auth_string=" << auth_string << std::endl;
+#endif
+
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
   try
   {
-    const Glib::ustring auth_string = create_auth_string(username, password);
     connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string);
     
     connection->statement_execute_non_select("SET DATESTYLE = 'ISO'");
@@ -84,7 +93,6 @@ Glib::RefPtr<Gnome::Gda::Connection> Postgres::attempt_connect(const Glib::ustri
   {
 #else
   std::auto_ptr<Glib::Error> error;
-  const Glib::ustring auth_string = create_auth_string(username, password);
   connection = Gnome::Gda::Connection::open_from_string("PostgreSQL", cnc_string, auth_string, Gnome::Gda::CONNECTION_OPTIONS_NONE, error);
   
   if(!error)
@@ -99,8 +107,8 @@ Glib::RefPtr<Gnome::Gda::Connection> Postgres::attempt_connect(const Glib::ustri
 #endif
 
 #ifdef GLOM_CONNECTION_DEBUG
-    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempt to connect to database failed on port=" << port << ", database=" << database << ": " << ex.what() << std::endl;
-    std::cout << "ConnectionPoolBackends::PostgresCentralHosted::attempt_connect(): Attempting to connect without specifying the database." << std::endl;
+    std::cout << "ConnectionPoolBackends::Postgres::attempt_connect(): Attempt to connect to database failed on port=" << port << ", database=" << database << ": " << ex.what() << std::endl;
+    std::cout << "ConnectionPoolBackends::Postgres::attempt_connect(): Attempting to connect without specifying the database." << std::endl;
 #endif
 
     Glib::ustring cnc_string = cnc_string_main + ";DB_NAME=" + default_database;
diff --git a/glom/libglom/connectionpool_backends/postgres_self.cc b/glom/libglom/connectionpool_backends/postgres_self.cc
index fb9d95c..792c7ef 100644
--- a/glom/libglom/connectionpool_backends/postgres_self.cc
+++ b/glom/libglom/connectionpool_backends/postgres_self.cc
@@ -76,15 +76,13 @@ namespace ConnectionPoolBackends
 
 //TODO: Do we need these sameuser lines?
 #define DEFAULT_CONFIG_PG_HBA_LOCAL \
-"local   all         postgres                          ident sameuser\n\
-\n\
-# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD\n\
+"# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD\n\
 \n\
 # local is for Unix domain socket connections only\n\
-# These are just here to make debugging with psql easier:\n\
+# trust allows connection from the current PC without a password:\n\
+local   all         all                               trust\n\
 local   all         all                               ident sameuser\n\
 local   all         all                               md5\n\
-local   all         postgres                          ident sameuser\n\
 \n\
 # TCP connections from the same computer, with a password:\n\
 # TODO: IPv6 too.\n\
diff --git a/glom/main.cc b/glom/main.cc
index aae48f5..f3e740e 100644
--- a/glom/main.cc
+++ b/glom/main.cc
@@ -220,6 +220,8 @@ bool check_pyglom_is_available_with_warning()
   Hildon::Note note(Hildon::NOTE_TYPE_INFORMATION, message);
   note.run();
 #endif //GLOM_ENABLE_MAEMO
+
+  return false;
 }
 
 bool check_pygda_is_available_with_warning()
@@ -238,6 +240,8 @@ bool check_pygda_is_available_with_warning()
   Hildon::Note note(Hildon::NOTE_TYPE_INFORMATION, message);
   note.run();
 #endif //GLOM_ENABLE_MAEMO
+
+  return false;
 }
 
 } //namespace Glom
diff --git a/glom/mode_design/users/dialog_users_list.cc b/glom/mode_design/users/dialog_users_list.cc
index c8ca4b5..2d0af3b 100644
--- a/glom/mode_design/users/dialog_users_list.cc
+++ b/glom/mode_design/users/dialog_users_list.cc
@@ -129,10 +129,7 @@ void Dialog_UsersList::on_button_user_remove()
         const Glib::ustring user = row[m_model_columns_users.m_col_name];
         if(!user.empty())
         {
-          Glib::ustring strQuery = "ALTER GROUP \"" + m_combo_group->get_active_text() + "\" DROP USER \"" + user + "\"";
-          const bool test = query_execute(strQuery);
-          if(!test)
-            std::cerr << "Dialog_UsersList::on_button_user_remove(): ALTER GROUP failed." << std::endl;
+          remove_user_from_group(user, m_combo_group->get_active_text());
 
           fill_list();
         }
@@ -168,11 +165,7 @@ void Dialog_UsersList::on_button_user_delete()
 
           if(response == Gtk::RESPONSE_OK)
           {
-            const Glib::ustring strQuery = "DROP USER \"" + user + "\"";
-            const bool test = query_execute(strQuery);
-            if(!test)
-              std::cerr << "Dialog_UsersList::on_button_user_delete(): DROP USER failed" << std::endl;
-
+            remove_user(user);
             fill_list();
           }
         }
@@ -276,23 +269,8 @@ void Dialog_UsersList::on_button_user_new()
   if(response != Gtk::RESPONSE_OK)
     return;
 
-
-  if(!user.empty() && !password.empty())
-  {
-    //Create the user:
-    Glib::ustring strQuery = "CREATE USER \"" + user + "\" PASSWORD '" + password + "'" ; //TODO: Escape the password.
-    bool test = query_execute(strQuery);
-    if(!test)
-       std::cerr << "Dialog_UsersList::on_button_user_new(): CREATE USER failed." << std::endl;
-
-    //Add it to the group:
-    strQuery = "ALTER GROUP \"" + m_combo_group->get_active_text() + "\" ADD USER \"" + user + "\"";
-    test = query_execute(strQuery);
-    if(!test)
-       std::cerr << "Dialog_UsersList::on_button_user_new(): ALTER GROUP failed." << std::endl;
-
-    fill_list();
-  }
+  add_user(user, password, m_combo_group->get_active_text() /* group */);
+  fill_list();
 }
 
 void Dialog_UsersList::on_button_user_edit()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]