1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) James Liggett 2007 <jrliggett@cox.net>
6 * anjuta is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include "svn-command.h"
27 struct _SvnCommandPriv
29 svn_client_ctx_t
*client_context
;
31 GQueue
*info_messages
;
33 gboolean main_thread_has_ui_lock
;
36 G_DEFINE_TYPE (SvnCommand
, svn_command
, ANJUTA_TYPE_ASYNC_COMMAND
);
39 svn_command_acquire_ui_lock (SvnCommand
*self
)
43 if (!self
->priv
->main_thread_has_ui_lock
)
45 got_lock
= g_mutex_trylock (self
->priv
->ui_lock
);
48 self
->priv
->main_thread_has_ui_lock
= TRUE
;
57 svn_command_release_ui_lock (GMutex
*ui_lock
)
59 g_mutex_unlock (ui_lock
);
60 g_mutex_free (ui_lock
);
66 /* In order to prevent deadlocking when Subversion prompts for something, we
67 * have to make sure that no GTK calls are made from the command threads by way
68 * of the authentication baton. To do this, the dialog code will be called from
71 /* svn_auth_simple_prompt_func_cb argumements */
74 svn_auth_cred_simple_t
**cred
;
78 svn_boolean_t may_save
;
83 /* svn_auth_ssl_server_trust_prompt_func_cb arguements */
86 svn_auth_cred_ssl_server_trust_t
**cred
;
89 apr_uint32_t failures
;
90 svn_auth_ssl_server_cert_info_t
*cert_info
;
91 svn_boolean_t may_save
;
97 simple_prompt (SimplePromptArgs
*args
)
99 GladeXML
* gxml
= glade_xml_new(GLADE_FILE
, "svn_user_auth", NULL
);
100 GtkWidget
* svn_user_auth
= glade_xml_get_widget(gxml
, "svn_user_auth");
101 GtkWidget
* auth_realm
= glade_xml_get_widget(gxml
, "auth_realm");
102 GtkWidget
* username_entry
= glade_xml_get_widget(gxml
, "username_entry");
103 GtkWidget
* password_entry
= glade_xml_get_widget(gxml
, "password_entry");
104 GtkWidget
* remember_pwd
= glade_xml_get_widget(gxml
, "remember_pwd");
105 svn_error_t
*err
= NULL
;
106 SvnCommand
*svn_command
;
108 gtk_dialog_set_default_response (GTK_DIALOG (svn_user_auth
), GTK_RESPONSE_OK
);
111 gtk_label_set_text (GTK_LABEL (auth_realm
), args
->realm
);
114 gtk_entry_set_text (GTK_ENTRY (username_entry
), args
->username
);
115 gtk_widget_grab_focus (password_entry
);
118 gtk_widget_set_sensitive(remember_pwd
, FALSE
);
120 /* Then the dialog is prompted to user and when user clicks ok, the
121 * values entered, i.e username, password and remember password (true
122 * by default) should be used to initialized the memebers below. If the
123 * user cancels the dialog, I think we return an error struct
124 * appropriately initialized. -- naba
127 switch (gtk_dialog_run(GTK_DIALOG(svn_user_auth
)))
129 case GTK_RESPONSE_OK
:
131 *args
->cred
= apr_pcalloc (args
->pool
, sizeof(*(args
->cred
)));
132 (*(args
->cred
))->may_save
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
134 (*(args
->cred
))->username
= apr_pstrdup (args
->pool
,
135 gtk_entry_get_text(GTK_ENTRY(username_entry
)));
136 (*(args
->cred
))->password
= apr_pstrdup (args
->pool
,
137 gtk_entry_get_text(GTK_ENTRY(password_entry
)));
143 err
= svn_error_create (SVN_ERR_AUTHN_CREDS_UNAVAILABLE
, NULL
,
144 _("Authentication canceled"));
148 gtk_widget_destroy (svn_user_auth
);
151 /* Release because main thread should already have the lock */
152 svn_command
= SVN_COMMAND (args
->baton
);
153 svn_command_unlock_ui (svn_command
);
159 ssl_server_trust_prompt (SSLServerTrustArgs
*args
)
161 GladeXML
* gxml
= glade_xml_new(GLADE_FILE
, "svn_server_trust", NULL
);
162 GtkWidget
* svn_server_trust
= glade_xml_get_widget(gxml
, "svn_server_trust");
163 GtkWidget
* auth_realm
= glade_xml_get_widget(gxml
, "realm_label");
164 GtkWidget
* server_info
= glade_xml_get_widget(gxml
, "server_info_label");
165 GtkWidget
* remember_check
= glade_xml_get_widget(gxml
, "remember_check");
166 svn_error_t
*err
= NULL
;
168 SvnCommand
*svn_command
;
171 gtk_label_set_text (GTK_LABEL (auth_realm
), args
->realm
);
173 info
= g_strconcat(_("Hostname: "), args
->cert_info
->hostname
, "\n",
174 _("Fingerprint: "), args
->cert_info
->fingerprint
, "\n",
175 _("Valid from: "), args
->cert_info
->valid_from
, "\n",
176 _("Valid until: "), args
->cert_info
->valid_until
, "\n",
177 _("Issuer DN: "), args
->cert_info
->issuer_dname
, "\n",
178 _("DER certificate: "), args
->cert_info
->ascii_cert
, "\n",
180 gtk_label_set_text (GTK_LABEL (server_info
), info
);
183 gtk_widget_set_sensitive(remember_check
, FALSE
);
185 gtk_dialog_set_default_response (GTK_DIALOG (svn_server_trust
), GTK_RESPONSE_YES
);
188 switch (gtk_dialog_run(GTK_DIALOG(svn_server_trust
)))
190 case GTK_RESPONSE_YES
:
191 *args
->cred
= apr_pcalloc (args
->pool
,
192 sizeof(*(args
->cred
)));
193 (*(args
->cred
))->may_save
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
196 /* TODO: Set bitmask for accepted_failures */
199 err
= svn_error_create (SVN_ERR_AUTHN_CREDS_UNAVAILABLE
, NULL
,
200 _("Authentication canceled"));
203 gtk_widget_destroy (svn_server_trust
);
206 /* Release because main thread should already have the lock */
207 svn_command
= SVN_COMMAND (args
->baton
);
208 svn_command_unlock_ui (svn_command
);
213 /* User authentication prompts handlers */
215 svn_auth_simple_prompt_func_cb (svn_auth_cred_simple_t
**cred
, void *baton
,
216 const char *realm
, const char *username
,
217 svn_boolean_t may_save
, apr_pool_t
*pool
)
219 SimplePromptArgs
*args
;
220 SvnCommand
*svn_command
;
223 args
= g_new0 (SimplePromptArgs
, 1);
226 args
->realm
= g_strdup (realm
);
227 args
->username
= g_strdup (username
);
228 args
->may_save
= may_save
;
231 svn_command
= SVN_COMMAND (baton
);
233 g_idle_add ((GSourceFunc
) simple_prompt
, args
);
235 svn_command_lock_ui (svn_command
);
236 svn_command_unlock_ui (svn_command
);
239 g_free (args
->realm
);
240 g_free (args
->username
);
248 svn_auth_ssl_server_trust_prompt_func_cb (svn_auth_cred_ssl_server_trust_t
**cred
,
249 void *baton
, const char *realm
,
250 apr_uint32_t failures
,
251 const svn_auth_ssl_server_cert_info_t
*cert_info
,
252 svn_boolean_t may_save
,
255 SSLServerTrustArgs
*args
;
256 SvnCommand
*svn_command
;
259 args
= g_new0 (SSLServerTrustArgs
, 1);
262 args
->realm
= g_strdup (realm
);
263 args
->failures
= failures
;
264 args
->cert_info
= g_memdup (cert_info
,
265 sizeof (svn_auth_ssl_server_cert_info_t
));
266 args
->may_save
= may_save
;
269 svn_command
= SVN_COMMAND (baton
);
271 g_idle_add_full (G_PRIORITY_HIGH_IDLE
,
272 (GSourceFunc
) ssl_server_trust_prompt
, args
, NULL
);
274 svn_command_lock_ui (svn_command
);
275 svn_command_unlock_ui (svn_command
);
278 g_free (args
->realm
);
279 g_free (args
->cert_info
);
286 svn_auth_ssl_client_cert_prompt_func_cb (svn_auth_cred_ssl_client_cert_t
**cred
,
287 void *baton
, const char *realm
,
288 svn_boolean_t may_save
,
292 /* Ask for the file where client certificate of authenticity is.
293 * I think it is some sort of private key. */
298 svn_auth_ssl_client_cert_pw_prompt_func_cb (svn_auth_cred_ssl_client_cert_pw_t
**cred
,
299 void *baton
, const char *realm
,
300 svn_boolean_t may_save
,
304 /* Prompt for password only. I think it is pass-phrase of the above key. */
305 return SVN_NO_ERROR
;;
308 /* Notification callback to handle notifications from Subversion itself */
310 on_svn_notify (gpointer baton
,
311 const svn_wc_notify_t
*notify
,
315 gchar
*action_message
;
316 gchar
*state_message
;
318 self
= SVN_COMMAND (baton
);
319 action_message
= NULL
;
320 state_message
= NULL
;
322 switch (notify
->action
)
324 case svn_wc_notify_delete
:
325 action_message
= g_strdup_printf (_("Deleted: %s"), notify
->path
);
327 case svn_wc_notify_add
:
328 action_message
= g_strdup_printf (_("Added: %s"), notify
->path
);
330 case svn_wc_notify_revert
:
331 action_message
= g_strdup_printf ("Reverted: %s", notify
->path
);
333 case svn_wc_notify_failed_revert
:
334 action_message
= g_strdup_printf ("Revert failed: %s",
337 case svn_wc_notify_resolved
:
338 action_message
= g_strdup_printf (_("Resolved: %s"), notify
->path
);
339 case svn_wc_notify_update_delete
:
340 action_message
= g_strdup_printf (_("Deleted: %s"), notify
->path
);
342 case svn_wc_notify_update_update
:
343 action_message
= g_strdup_printf (_("Updated: %s"), notify
->path
);
345 case svn_wc_notify_update_add
:
346 action_message
= g_strdup_printf (_("Added: %s"), notify
->path
);
348 case svn_wc_notify_update_external
:
349 action_message
= g_strdup_printf (_("Externally Updated: %s"),
352 case svn_wc_notify_commit_modified
:
353 action_message
= g_strdup_printf ("Commit Modified: %s",
356 case svn_wc_notify_commit_added
:
357 action_message
= g_strdup_printf ("Commit Added: %s", notify
->path
);
359 case svn_wc_notify_commit_deleted
:
360 action_message
= g_strdup_printf ("Commit Deleted: %s",
363 case svn_wc_notify_commit_replaced
:
364 action_message
= g_strdup_printf ("Commit Replaced: %s",
367 case svn_wc_notify_copy
:
368 action_message
= g_strdup_printf ("Created File: %s", notify
->path
);
376 svn_command_push_info (self
, action_message
);
377 g_free (action_message
);
380 switch (notify
->content_state
)
382 case svn_wc_notify_state_changed
:
383 state_message
= g_strdup_printf (_("Modified: %s"), notify
->path
);
385 case svn_wc_notify_state_merged
:
386 state_message
= g_strdup_printf (_("Merged: %s"), notify
->path
);
388 case svn_wc_notify_state_conflicted
:
389 state_message
= g_strdup_printf (_("Conflicted: %s"),
392 case svn_wc_notify_state_missing
:
393 state_message
= g_strdup_printf (_("Missing: %s"), notify
->path
);
395 case svn_wc_notify_state_obstructed
:
396 state_message
= g_strdup_printf (_("Obstructed: %s"), notify
->path
);
404 svn_command_push_info (self
, state_message
);
405 g_free (state_message
);
410 svn_command_init (SvnCommand
*self
)
412 svn_auth_baton_t
*auth_baton
;
413 apr_array_header_t
*providers
;
414 svn_auth_provider_object_t
*provider
;
416 self
->priv
= g_new0 (SvnCommandPriv
, 1);
418 self
->priv
->pool
= svn_pool_create (NULL
);
419 svn_client_create_context (&self
->priv
->client_context
, self
->priv
->pool
);
420 self
->priv
->client_context
->notify_func2
= on_svn_notify
;
421 self
->priv
->client_context
->notify_baton2
= self
;
423 svn_config_get_config (&(self
->priv
->client_context
)->config
,
424 NULL
, /* default dir */
427 self
->priv
->info_messages
= g_queue_new ();
428 self
->priv
->ui_lock
= g_mutex_new ();
430 /* Make sure that the main thread holds the lock */
431 g_idle_add ((GSourceFunc
) svn_command_acquire_ui_lock
, self
);
433 /* Fill in the auth baton callbacks */
434 providers
= apr_array_make (self
->priv
->pool
, 1,
435 sizeof (svn_auth_provider_object_t
*));
437 /* Provider that authenticates username/password from ~/.subversion */
438 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
439 svn_client_get_simple_provider (&provider
, self
->priv
->pool
);
440 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
442 /* Provider that authenticates server trust from ~/.subversion */
443 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
444 svn_client_get_ssl_server_trust_file_provider (&provider
, self
->priv
->pool
);
445 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
447 /* Provider that authenticates client cert from ~/.subversion */
448 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
449 svn_client_get_ssl_client_cert_file_provider (&provider
, self
->priv
->pool
);
450 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
452 /* Provider that authenticates client cert password from ~/.subversion */
453 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
454 svn_client_get_ssl_client_cert_pw_file_provider (&provider
,
456 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
458 /* Provider that prompts for username/password */
459 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
460 svn_client_get_simple_prompt_provider(&provider
,
461 svn_auth_simple_prompt_func_cb
,
462 self
, 3, self
->priv
->pool
);
463 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
465 /* Provider that prompts for server trust */
466 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
467 svn_client_get_ssl_server_trust_prompt_provider (&provider
,
468 svn_auth_ssl_server_trust_prompt_func_cb
,
471 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
473 /* Provider that prompts for client certificate file */
474 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
475 svn_client_get_ssl_client_cert_prompt_provider (&provider
,
476 svn_auth_ssl_client_cert_prompt_func_cb
,
477 NULL
, 3, self
->priv
->pool
);
478 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
480 /* Provider that prompts for client certificate file password */
481 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
482 svn_client_get_ssl_client_cert_pw_prompt_provider (&provider
,
483 svn_auth_ssl_client_cert_pw_prompt_func_cb
,
486 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
488 svn_auth_open (&auth_baton
, providers
, self
->priv
->pool
);
489 self
->priv
->client_context
->auth_baton
= auth_baton
;
494 svn_command_finalize (GObject
*object
)
497 GList
*current_message_line
;
499 self
= SVN_COMMAND (object
);
501 svn_pool_clear (self
->priv
->pool
);
503 current_message_line
= self
->priv
->info_messages
->head
;
505 while (current_message_line
)
507 g_free (current_message_line
->data
);
508 current_message_line
= g_list_next (current_message_line
);
511 g_idle_add ((GSourceFunc
) svn_command_release_ui_lock
, self
->priv
->ui_lock
);
513 g_queue_free (self
->priv
->info_messages
);
516 G_OBJECT_CLASS (svn_command_parent_class
)->finalize (object
);
520 svn_command_class_init (SvnCommandClass
*klass
)
522 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
524 object_class
->finalize
= svn_command_finalize
;
529 svn_command_push_info (SvnCommand
*self
, gchar
*message
)
531 anjuta_async_command_lock (ANJUTA_ASYNC_COMMAND (self
));
532 g_queue_push_tail (self
->priv
->info_messages
, g_strdup (message
));
533 anjuta_async_command_unlock (ANJUTA_ASYNC_COMMAND (self
));
535 anjuta_command_notify_data_arrived (ANJUTA_COMMAND (self
));
539 svn_command_get_info_queue (SvnCommand
*self
)
541 return self
->priv
->info_messages
;
545 svn_command_set_error (SvnCommand
*self
, svn_error_t
*error
)
547 GString
*error_string
;
548 svn_error_t
*current_error
;
549 gchar
*error_c_string
;
551 error_string
= g_string_new ("");
552 current_error
= error
;
554 while (current_error
)
556 g_string_append (error_string
, current_error
->message
);
558 if (current_error
->child
)
559 g_string_append_c (error_string
, '\n');
561 current_error
= current_error
->child
;
564 error_c_string
= g_string_free (error_string
, FALSE
);
565 anjuta_async_command_set_error_message (ANJUTA_COMMAND (self
),
568 g_free (error_c_string
);
572 svn_command_get_client_context (SvnCommand
*self
)
574 return self
->priv
->client_context
;
578 svn_command_get_pool (SvnCommand
*self
)
580 return self
->priv
->pool
;
584 svn_command_lock_ui (SvnCommand
*self
)
586 g_mutex_lock (self
->priv
->ui_lock
);
588 /* Have the main thread acquire the lock as soon as the other thread is done
589 * with it. The main thread should *not* acqure the lock, only release
591 self
->priv
->main_thread_has_ui_lock
= FALSE
;
592 g_idle_add ((GSourceFunc
) svn_command_acquire_ui_lock
, self
);
596 svn_command_unlock_ui (SvnCommand
*self
)
598 g_mutex_unlock (self
->priv
->ui_lock
);
602 svn_command_get_revision (gchar
*revision
)
604 svn_opt_revision_t
* svn_revision
;
606 svn_revision
= g_new0 (svn_opt_revision_t
, 1);
608 /* FIXME: Parse the revision string */
609 svn_revision
->kind
= svn_opt_revision_head
;
615 svn_command_copy_path_list (GList
*list
)
625 new_list
= g_list_append (new_list
, g_strdup (current_path
->data
));
626 current_path
= g_list_next (current_path
);
633 svn_command_free_path_list (GList
*list
)
641 g_free (current_path
->data
);
642 current_path
= g_list_next (current_path
);