Ejemplo n.º 1
0
void
mdm_save_customlist_data (const gchar *file,
			  const gchar *key,
			  const gchar *id)
{
	GKeyFile *cfg;

	mdm_debug ("Saving custom configuration data to file=%s, key=%s",
		file, key);
	cfg = mdm_common_config_load (file, NULL);
	if (cfg == NULL) {
		gint fd = -1;
                mdm_debug ("creating file: %s", file);
		VE_IGNORE_EINTR (fd = g_open (file,
			O_CREAT | O_TRUNC | O_RDWR, 0644));

		if (fd < 0)
			return;

		write (fd, "\n", 2);
		close (fd);
		cfg = mdm_common_config_load (file, NULL);
		if (cfg == NULL) {
			return;
		}
	}

	g_key_file_set_string (cfg, "GreeterInfo", key, ve_sure_string (id));
	mdm_common_config_save (cfg, file, NULL);
	g_key_file_free (cfg);
}
Ejemplo n.º 2
0
/* Callback function for list style custom lists */
static void
list_selected (GtkTreeSelection *selection, GreeterItemInfo *item)
{
  GtkTreeModel *tm = NULL;
  GtkTreeIter iter = {0};
  char *id = NULL;
  char *file;

  if (ve_string_empty (item->id))
    return;

  if (gtk_tree_selection_get_selected (selection, &tm, &iter))
    {
      gtk_tree_model_get (tm, &iter, GREETER_LIST_ID,
                          &id, -1);
    }

  /* 
   * Note for session and language we are using the id to store the
   * value to pass in.
   */
  if (strcmp (item->id, "session") == 0)
    {
      if (id != NULL)
        greeter_set_session (id);
    }
  else if (strcmp (item->id, "language") == 0)
    {
      if (id != NULL)
        mdm_lang_set_restart_dialog (id);
    }
  else
    {
      if (DOING_MDM_DEVELOPMENT)
        return;

      file = g_strdup_printf ("%s/%s.GreeterInfo",
        ve_sure_string (mdm_config_get_string (MDM_KEY_SERV_AUTHDIR)),
        ve_sure_string (g_getenv ("DISPLAY")));

      mdm_save_customlist_data (file, item->id, id);
   }
}
Ejemplo n.º 3
0
static int
mdm_verify_standalone_pam_conv (int num_msg,
				MDM_PAM_QUAL struct pam_message **msg,
				struct pam_response **resp,
				void *appdata_ptr)
{
	int replies = 0;
	int i;
        char *text;
        char *question_msg;
	struct pam_response *reply = NULL;

	if (pamh == NULL)
		return PAM_CONV_ERR;

	reply = malloc (sizeof (struct pam_response) * num_msg);

	if (reply == NULL)
		return PAM_CONV_ERR;

	memset (reply, 0, sizeof (struct pam_response) * num_msg);

	for (replies = 0; replies < num_msg; replies++) {
		const char *m = (*msg)[replies].msg;
		m = perhaps_translate_message (m);
		switch ((*msg)[replies].msg_style) {

		case PAM_PROMPT_ECHO_ON:
			if (extra_standalone_message != NULL)
				text = g_strdup_printf
					("%s%s", extra_standalone_message,
					 m);
			else
				text = g_strdup (m);

			/* PAM requested textual input with echo on */
			question_msg = g_strdup_printf ("question_msg=%s$$echo=%d", text, TRUE);

			mdm_slave_send_string (MDM_SOP_SHOW_QUESTION_DIALOG, question_msg);

			g_free (question_msg);
			g_free (text);

			reply[replies].resp_retcode = PAM_SUCCESS;
			if (mdm_ack_question_response) {
				reply[replies].resp = strdup (ve_sure_string (mdm_ack_question_response));
				g_free (mdm_ack_question_response);
				mdm_ack_question_response = NULL;
			} else
				reply[replies].resp = NULL;

			break;

		case PAM_PROMPT_ECHO_OFF:
			if (extra_standalone_message != NULL)
				text = g_strdup_printf
					("%s%s", extra_standalone_message,
					 m);
			else
				text = g_strdup (m);

			/* PAM requested textual input with echo off */
			question_msg = g_strdup_printf ("question_msg=%s$$echo=%d", text, FALSE);

			mdm_slave_send_string (MDM_SOP_SHOW_QUESTION_DIALOG, question_msg);

			g_free (question_msg);
			g_free (text);

			reply[replies].resp_retcode = PAM_SUCCESS;
			if (mdm_ack_question_response) {
				reply[replies].resp = strdup (ve_sure_string (mdm_ack_question_response));
				g_free (mdm_ack_question_response);
				mdm_ack_question_response = NULL;
			} else
				reply[replies].resp = NULL;

			break;

		case PAM_ERROR_MSG:
			/* PAM sent a message that should displayed to the user */
			mdm_errorgui_error_box (cur_mdm_disp,
						GTK_MESSAGE_ERROR,
						m);
			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = NULL;
			break;

		case PAM_TEXT_INFO:
			/* PAM sent a message that should displayed to the user */
			mdm_errorgui_error_box (cur_mdm_disp,
						GTK_MESSAGE_INFO,
						m);
			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = NULL;
			break;

		default:
			/* PAM has been smoking serious crack */
			for (i = 0; i < replies; i++)
				if (reply[replies].resp != NULL)
					free (reply[replies].resp);
			free (reply);
			return PAM_CONV_ERR;
		}

	}

	*resp = reply;
	return PAM_SUCCESS;
}
Ejemplo n.º 4
0
static int
mdm_verify_pam_conv (int num_msg,
		     MDM_PAM_QUAL struct pam_message **msg,
		     struct pam_response **resp,
		     void *appdata_ptr)
{
	int replies = 0;
	int i;
	char *s = NULL;
	struct pam_response *reply = NULL;
	MDM_PAM_QUAL void *p;
	const char *login;

	if (pamh == NULL)
		return PAM_CONV_ERR;

	/* Should never happen unless PAM is on crack and keeps asking questions
	   after we told it to go away.  So tell it to go away again and
	   maybe it will listen */
	/* well, it actually happens if there are multiple pam modules
	 * with conversations */
	if ( ! mdm_slave_action_pending () || selected_user)
		return PAM_CONV_ERR;

	reply = malloc (sizeof (struct pam_response) * num_msg);

	if (reply == NULL)
		return PAM_CONV_ERR;

	memset (reply, 0, sizeof (struct pam_response) * num_msg);

	/* Here we set the login if it wasn't already set,
	 * this is kind of anal, but this way we guarantee that
	 * the greeter always is up to date on the login */
	if (pam_get_item (pamh, PAM_USER, &p) == PAM_SUCCESS) {
		login = (const char *)p;
		mdm_slave_greeter_ctl_no_ret (MDM_SETLOGIN, login);
	}

	/* Workaround to avoid mdm messages being logged as PAM_pwdb */
	mdm_log_shutdown ();
	mdm_log_init ();

	for (replies = 0; replies < num_msg; replies++) {
		const char *m = (*msg)[replies].msg;
		m = perhaps_translate_message (m);

		switch ((*msg)[replies].msg_style) {

			/* PAM requested textual input with echo on */
		case PAM_PROMPT_ECHO_ON:
			if (strcmp (m, _("Username:"******"Please enter your username"));
					s = mdm_slave_greeter_ctl (MDM_PROMPT, m);
					/* this will clear the message */
					mdm_slave_greeter_ctl_no_ret (MDM_MSG, "");
				}
			} else {
				s = mdm_slave_greeter_ctl (MDM_PROMPT, m);
			}

			if (mdm_slave_greeter_check_interruption ()) {
				g_free (s);
				for (i = 0; i < replies; i++)
					if (reply[replies].resp != NULL)
						free (reply[replies].resp);
				free (reply);
				return PAM_CONV_ERR;
			}

			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = strdup (ve_sure_string (s));
			g_free (s);
			break;

		case PAM_PROMPT_ECHO_OFF:
			if (strcmp (m, _("Password:")) == 0)
				did_we_ask_for_password = TRUE;
			/* PAM requested textual input with echo off */
			s = mdm_slave_greeter_ctl (MDM_NOECHO, m);
			if (mdm_slave_greeter_check_interruption ()) {
				g_free (s);
				for (i = 0; i < replies; i++)
					if (reply[replies].resp != NULL)
						free (reply[replies].resp);
				free (reply);
				return PAM_CONV_ERR;
			}
			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = strdup (ve_sure_string (s));
			g_free (s);
			break;

		case PAM_ERROR_MSG:
			/* PAM sent a message that should displayed to the user */
			mdm_slave_greeter_ctl_no_ret (MDM_ERRDLG, m);
			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = NULL;
			break;

		case PAM_TEXT_INFO:
			/* PAM sent a message that should displayed to the user */
			mdm_slave_greeter_ctl_no_ret (MDM_MSG, m);
			reply[replies].resp_retcode = PAM_SUCCESS;
			reply[replies].resp = NULL;
			break;

		default:
			/* PAM has been smoking serious crack */
			for (i = 0; i < replies; i++)
				if (reply[replies].resp != NULL)
					free (reply[replies].resp);
			free (reply);
			return PAM_CONV_ERR;
		}

	}

	*resp = reply;
	return PAM_SUCCESS;
}
Ejemplo n.º 5
0
static char *
do_command (int fd, const char *command, gboolean get_response)
{
	GString *str;
	char buf[1];
	char *cstr;
	int ret;
#ifndef MSG_NOSIGNAL
	void (*old_handler)(int);
#endif

        mdm_common_debug ("Sending command: '%s'", command);

	cstr = g_strdup_printf ("%s\n", command);

#ifdef MSG_NOSIGNAL
	ret = send (fd, cstr, strlen (cstr), MSG_NOSIGNAL);
#else
	old_handler = signal (SIGPIPE, SIG_IGN);
	ret = send (fd, cstr, strlen (cstr), 0);
	signal (SIGPIPE, old_handler);
#endif
	g_free (cstr);

	num_cmds++;

	if (ret < 0) {
		if ( !quiet)
			mdm_common_debug ("Command failed, no data returned");
		return NULL;
	}

	/* No need to print debug, this is only used when closing */
	if ( ! get_response)
		return NULL;

	str = g_string_new (NULL);
	while (read (fd, buf, 1) == 1 &&
	       buf[0] != '\n') {
		g_string_append_c (str, buf[0]);
	}

        mdm_common_debug ("  Got response: '%s'", str->str);

	cstr = str->str;
	g_string_free (str, FALSE);

	/*
	 * If string is empty, then the daemon likely closed the connection 
	 * because of too many subconnections.  At any rate the daemon should
	 * not return an empty string.  All return values should start with
	 * "OK" or "ERROR".  Daemon should never complain about too many
	 * messages since the slave keeps track of the number of commands sent
	 * and should not send too many, but it does not hurt to check and
	 * manage it if it somehow happens.  In either case return NULL
	 * instead so the caller can try again.
	 */
	if (ve_string_empty (cstr) ||
	    strcmp (ve_sure_string (cstr), "ERROR 200 Too many messages") == 0) {
		if ( !quiet)
			mdm_common_debug ("Command failed, daemon busy.");
		g_free (cstr);
		return NULL;
	}

	return cstr;
}
Ejemplo n.º 6
0
static char *
mdmcomm_call_mdm_real (const char *command,
		       const char *auth_cookie,
		       const char *min_version,
		       int tries,
		       int try_start)
{
	char *ret;

	/*
         * If already sent the max number of commands, close the connection
         * and reopen.  Subtract 1 to allow the "CLOSE" to get through.
         */
	if (num_cmds == (MDM_SUP_MAX_MESSAGES - 1)) {
		mdm_common_debug ("  Closing and reopening connection.");
		do_command (comm_fd, MDM_SUP_CLOSE, FALSE);
		VE_IGNORE_EINTR (close (comm_fd));
		comm_fd  = 0;
		num_cmds = 0;
	}

	if (tries <= 0) {
		if ( !quiet)
			mdm_common_debug ("  Command failed %d times, aborting.", try_start);
		return NULL;
	}

	if (!quiet && tries != try_start) {
		mdm_common_debug ("  Trying failed command again.  Try %d of %d.",
				  (try_start - tries + 1), try_start);
	}

	if (comm_fd <= 0) {
		struct sockaddr_un addr;
		strcpy (addr.sun_path, MDM_SUP_SOCKET);
		addr.sun_family = AF_UNIX;
		comm_fd = socket (AF_UNIX, SOCK_STREAM, 0);
		if (comm_fd < 0) {
			if ( !quiet)
				mdm_common_debug ("  Failed to open socket");

			return mdmcomm_call_mdm_real (command, auth_cookie, min_version, tries - 1, try_start);
		}

		if (connect (comm_fd, (struct sockaddr *)&addr, sizeof (addr)) < 0) {


			/*
			 * If there is a failure on connect, there are probably
                         * other clients fighting for the connection, so sleep
                         * for 1 second before retry to avoid failing over and
                         * over in a tight loop.
                         *
                         * Only do this if allow_sleep is true.  allow_sleep
                         * will get set to FALSE if the first call to this 
                         * function fails all retries.
			 */
			if (allow_sleep == TRUE) {

				did_sleep_on_failure = TRUE;

				/*
				 * Only actualy sleep if we are going to try
				 * again.
				 */
				if (tries > 1) {
					if ( !quiet)
						mdm_common_debug ("  Failed to connect to socket, sleep 1 second and retry");
					sleep (1);
				}
			} else {
				if ( !quiet)
					mdm_common_debug ("  Failed to connect to socket, not sleeping");
			}
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			return mdmcomm_call_mdm_real (command, auth_cookie,
						      min_version, tries - 1, try_start);
		}

		/*
                 * If we get this far, then even if we did sleep in the past,
		 * we did get a connection, so no need to prevent future
		 * sleeps if required.
                 */
		allow_sleep          = TRUE;
                did_sleep_on_failure = FALSE;

		/* Version check first - only check first time */
		ret = do_command (comm_fd, MDM_SUP_VERSION, TRUE);
		if (ret == NULL) {
			if ( !quiet)
				mdm_common_debug ("  Version check failed");
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			return mdmcomm_call_mdm_real (command, auth_cookie,
						      min_version, tries - 1, try_start);
		}
		if (strncmp (ret, "MDM ", strlen ("MDM ")) != 0) {
			if ( !quiet)
				mdm_common_debug ("  Version check failed, bad name");

			g_free (ret);
			do_command (comm_fd, MDM_SUP_CLOSE, FALSE);
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			return NULL;
		}
		if ( ! version_ok_p (&ret[4], min_version)) {
			if ( !quiet)
				mdm_common_debug ("  Version check failed, bad version");
			g_free (ret);
			do_command (comm_fd, MDM_SUP_CLOSE, FALSE);
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			return NULL;
		}
		g_free (ret);
	}

	/* require authentication */
	if (auth_cookie != NULL)  {
		char *auth_cmd = g_strdup_printf
			(MDM_SUP_AUTH_LOCAL " %s", auth_cookie);
		ret = do_command (comm_fd, auth_cmd, TRUE);
		g_free (auth_cmd);
		if (ret == NULL) {
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			return mdmcomm_call_mdm_real (command, auth_cookie,
						      min_version, tries - 1, try_start);
		}
		/* not auth'ed */
		if (strcmp (ve_sure_string (ret), "OK") != 0) {
			if ( !quiet)
				mdm_common_debug ("  Error, auth check failed");
			do_command (comm_fd, MDM_SUP_CLOSE, FALSE);
			VE_IGNORE_EINTR (close (comm_fd));
			comm_fd = 0;
			/* returns the error */
			return ret;
		}
		g_free (ret);
	}

	ret = do_command (comm_fd, command, TRUE);
	if (ret == NULL) {
		VE_IGNORE_EINTR (close (comm_fd));
		comm_fd = 0;
		return mdmcomm_call_mdm_real (command, auth_cookie,
					      min_version, tries - 1, try_start);
	}

	/*
	 * We want to leave the connection open if bulk_acs is set to
	 * true, so clients can read as much config data in one 
	 * sockets connection when it is set.  This requires that
	 * MDM client programs ensure that they call the bulk_start
	 * and bulk_stop functions around blocks of code that
	 * need to read data in bulk.  If a client reads config data
	 * outside of the bulk_start/stop functions, then this
	 * will just negatively affect performance since an additional
	 * socket will be opened to read that config data.
	 */
	if (bulk_acs == FALSE) {
		do_command (comm_fd, MDM_SUP_CLOSE, FALSE);
		VE_IGNORE_EINTR (close (comm_fd));
		comm_fd = 0;
	}

	return ret;
}
Ejemplo n.º 7
0
/**
 * mdm_config_get_bool
 *
 * Gets int configuration value from daemon via GET_CONFIG
 * socket command.  It stores the value in a hash so subsequent
 * access is faster.
 */
static gboolean
_mdm_config_get_bool (const gchar *key,
		      gboolean reload,
		      gboolean *changed)
{
	gboolean *hashretval = NULL;
	gchar    *result;
	gboolean temp;

        if (bool_hash == NULL)
           bool_hash = g_hash_table_new (g_str_hash, g_str_equal);

	hashretval = mdm_config_hash_lookup (bool_hash, key);
	if (reload == FALSE && hashretval != NULL)
		return *hashretval;

	result = mdm_config_get_result (key);

	if ( ! result || ve_string_empty (result) ||
	    strncmp (result, "OK ", 3) != 0) {

		gchar *getdefault;

		mdm_common_error ("Could not access configuration key <%s>", key);

		/* Return the compiled in value associated with the key, if available. */
		getdefault = strchr (key, '=');
		if (getdefault != NULL)
			getdefault++;

		/* Same logic as used in ve_config_get_bool */
		if (getdefault != NULL &&
		   (getdefault[0] == 'T' ||
		    getdefault[0] == 't' ||
		    getdefault[0] == 'Y' ||
		    getdefault[0] == 'y' ||
		    atoi (getdefault) != 0)) {
			temp = TRUE;
			mdm_common_error ("Using compiled in value <TRUE> for <%s>", key);
		} else {
			temp = FALSE;
			mdm_common_error ("Using compiled in value <FALSE> for <%s>", key);
		}
	} else {

		/* skip the "OK " */
		if (strcmp (ve_sure_string (result + 3), "true") == 0)
			temp = TRUE;
		else
			temp = FALSE;
	}

	if (result)
		g_free (result);

	if (hashretval == NULL) {
		gboolean *boolval = g_new0 (gboolean, 1);
		*boolval          = temp;
		mdm_config_add_hash (bool_hash, key, boolval);

		if (changed != NULL)
			*changed = TRUE;

		return *boolval;
	} else {
		if (changed != NULL) {
			if (*hashretval != temp)
				*changed = TRUE;
			else
				*changed = FALSE;
		}

		*hashretval = temp;
		return *hashretval;
	}
}
Ejemplo n.º 8
0
/**
 * mdm_config_get_string
 *
 * Gets string configuration value from daemon via GET_CONFIG
 * socket command.  It stores the value in a hash so subsequent
 * access is faster.
 */
static gchar *
_mdm_config_get_string (const gchar *key,
			gboolean reload,
			gboolean *changed,
			gboolean doing_translated)
{
	gchar *hashretval = NULL;
	gchar *result = NULL;
	gchar *temp;

        if (string_hash == NULL)
		string_hash = g_hash_table_new (g_str_hash, g_str_equal);

	hashretval = mdm_config_hash_lookup (string_hash, key);

	if (reload == FALSE && hashretval != NULL)
		return hashretval;

	result = mdm_config_get_result (key);

	if ( ! result || ve_string_empty (result) ||
	    strncmp (result, "OK ", 3) != 0) {

		gchar *getdefault;

		/*
		 * If looking for a translated string, and not found, just return
		 * NULL.
		 */
		if (doing_translated) {
			if (result)
				g_free (result);
			return NULL;
		}

		mdm_common_error ("Could not access configuration key <%s>", key);

		/* Return the compiled in value associated with the key, if available. */
		getdefault = strchr (key, '=');
		if (getdefault != NULL)
			getdefault++;

		temp = g_strdup (getdefault);

		mdm_common_error ("Using compiled in value <%s> for <%s>", temp, key);
	} else {

		/* skip the "OK " */
		temp = g_strdup (result + 3);
	}

	if (result)
		g_free (result);

	if (hashretval == NULL) {

		if (changed != NULL)
			*changed = TRUE;

		mdm_config_add_hash (string_hash, key, temp);
	} else {
		if (changed != NULL) {
			if (strcmp (ve_sure_string (hashretval), temp) != 0)
				*changed = TRUE;
			else
				*changed = FALSE;
		}
		g_hash_table_replace (string_hash, (void *)key, temp);
	}
	return temp;
}
Ejemplo n.º 9
0
/**
 * mdm_config_get_xservers
 *
 * Calls daemon to get xserver config.
 */
GSList *
mdm_config_get_xservers (gboolean flexible)
{
	GSList *xservers = NULL;
        gchar **splitstr, **sec;
	gchar *command = NULL;
	gchar *result  = NULL;
	gchar *temp;

	command = g_strdup_printf ("GET_SERVER_LIST");
	result = mdmcomm_call_mdm (command, NULL /* auth cookie */,
		"1.0.0.0", comm_tries);

	g_free (command);

	if (! result || ve_string_empty (result) ||
	    strncmp (result, "OK ", 3) != 0) {

		mdm_common_error ("Could not access xserver configuration");

		if (result)
			g_free (result);
		return NULL;
	}

	/* skip the "OK " */
	splitstr = g_strsplit (result + 3, ";", 0);
	sec      = splitstr;
	g_free (result);

	while (sec != NULL && *sec != NULL) {
		MdmXserver *svr = g_new0 (MdmXserver, 1);

		temp = mdm_config_get_xserver_details (*sec, "ID");
		if (temp == NULL) {
			g_free (svr);
			continue;
		}
		svr->id = temp;
		temp = mdm_config_get_xserver_details (*sec, "NAME");
		if (temp == NULL) {
			g_free (svr);
			continue;
		}
		svr->name = temp;
		temp = mdm_config_get_xserver_details (*sec, "COMMAND");
		if (temp == NULL) {
			g_free (svr);
			continue;
		}
		svr->command = temp;

		temp = mdm_config_get_xserver_details (*sec, "FLEXIBLE");
		if (temp == NULL) {
			g_free (svr);
			continue;
		} else if (g_strncasecmp (ve_sure_string (temp), "true", 4) == 0)
			svr->flexible = TRUE;
		else
			svr->flexible = FALSE;
		g_free (temp);

		temp = mdm_config_get_xserver_details (*sec, "CHOOSABLE");
		if (temp == NULL) {
			g_free (svr);
			continue;
		} else if (g_strncasecmp (temp, "true", 4) == 0)
			svr->choosable = TRUE;
		else
			svr->choosable = FALSE;
		g_free (temp);

		temp = mdm_config_get_xserver_details (*sec, "HANDLED");
		if (temp == NULL) {
			g_free (svr);
			continue;
		} else if (g_strncasecmp (temp, "true", 4) == 0)
			svr->handled = TRUE;
		else
			svr->handled = FALSE;
		g_free (temp);

		temp = mdm_config_get_xserver_details (*sec, "CHOOSER");
		if (temp == NULL) {
			g_free (svr);
			continue;
		} else if (g_strncasecmp (temp, "true", 4) == 0)
			svr->chooser = TRUE;
		else
			svr->chooser = FALSE;
		g_free (temp);

		temp = mdm_config_get_xserver_details (*sec, "PRIORITY");
		if (temp == NULL) {
			g_free (svr);
			continue;
		} else {
			svr->priority = atoi (temp);
		}
		g_free (temp);

		sec++;

		/* If only flexible was requested, then skip if not flexible */
		if (flexible && !svr->flexible) {
			g_free (svr);
			continue;
		}

		xservers = g_slist_append (xservers, svr);
	}

	g_strfreev (splitstr);
	return xservers;
}
Ejemplo n.º 10
0
/* Callback function for combo style custom lists */ 
static void
combo_selected (GtkComboBox *combo, GreeterItemInfo *item)
{
  char  *id = NULL;
  char  *file;
  char  *active;

  if (ve_string_empty (item->id))
    return;
 
  active = gtk_combo_box_get_active_text (combo);

  if (strcmp (item->id, "session") == 0)
    {
      combo_session_selected (active);
    }
  else if (strcmp (item->id, "language") == 0)
    {
      /*
       * Since combo boxes can't store the ID value, have to do some
       * extra work to figure out which row is selected.
       */
      GtkListStore *lang_model = mdm_lang_get_model ();
      GtkTreeIter iter;
      char *name, *untranslated, *lang_display_name, *locale_name;
      gboolean valid;

      valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (lang_model),
	 &iter);
      while (valid)
        {
          gtk_tree_model_get (GTK_TREE_MODEL (lang_model), &iter,
             TRANSLATED_NAME_COLUMN, &name,
             UNTRANSLATED_NAME_COLUMN, &untranslated,
             LOCALE_COLUMN, &locale_name, -1);

          if (untranslated)
             lang_display_name = g_strdup_printf ("%s (%s)", name, untranslated);
          else
             lang_display_name = g_strdup (name);

          if (strcmp (lang_display_name, active) == 0)
            {
              mdm_lang_set_restart_dialog (locale_name);
              break;
            }
          g_free (lang_display_name);
          valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (lang_model), &iter);
        }
    }
  else
    {
      if (DOING_MDM_DEVELOPMENT)
        return;

      id   = gtk_combo_box_get_active_text (combo);
      file = g_strdup_printf ("%s/%s.GreeterInfo",
             ve_sure_string (mdm_config_get_string (MDM_KEY_SERV_AUTHDIR)),
             ve_sure_string (g_getenv ("DISPLAY")));

      mdm_save_customlist_data (file, item->id, id);
   }
}