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
;
32 GCond
*dialog_finished_condition
;
33 GMutex
*dialog_finished_lock
;
34 gboolean dialog_finished
;
38 G_DEFINE_TYPE (SvnCommand
, svn_command
, ANJUTA_TYPE_ASYNC_COMMAND
);
42 /* In order to prevent deadlocking when Subversion prompts for something, we
43 * have to make sure that no GTK calls are made from the command threads by way
44 * of the authentication baton. To do this, the dialog code will be called from
47 /* svn_auth_simple_prompt_func_cb argumements */
50 svn_auth_cred_simple_t
**cred
;
54 svn_boolean_t may_save
;
59 /* svn_auth_ssl_server_trust_prompt_func_cb arguements */
62 svn_auth_cred_ssl_server_trust_t
**cred
;
65 apr_uint32_t failures
;
66 svn_auth_ssl_server_cert_info_t
*cert_info
;
67 svn_boolean_t may_save
;
73 /* Idle destroy functions. The command threads should wait for the dialogs to
74 * finish before continuing. These functions signals the condition structure,
75 * allowing them to continue. These functions are executed on the main thread */
78 on_simple_prompt_finished (SimplePromptArgs
*args
)
82 self
= SVN_COMMAND (args
->baton
);
84 g_mutex_lock (self
->priv
->dialog_finished_lock
);
86 self
->priv
->dialog_finished
= TRUE
;
87 g_cond_signal (self
->priv
->dialog_finished_condition
);
89 g_mutex_unlock (self
->priv
->dialog_finished_lock
);
93 on_ssl_server_trust_prompt_finished (SSLServerTrustArgs
*args
)
97 self
= SVN_COMMAND (args
->baton
);
99 g_mutex_lock (self
->priv
->dialog_finished_lock
);
101 self
->priv
->dialog_finished
= TRUE
;
102 g_cond_signal (self
->priv
->dialog_finished_condition
);
104 g_mutex_unlock (self
->priv
->dialog_finished_lock
);
108 simple_prompt (SimplePromptArgs
*args
)
110 GtkBuilder
* bxml
= gtk_builder_new ();
111 GtkWidget
* svn_user_auth
;
112 GtkWidget
* auth_realm
;
113 GtkWidget
* username_entry
;
114 GtkWidget
* password_entry
;
115 GtkWidget
* remember_pwd
;
116 svn_error_t
*err
= NULL
;
117 SvnCommand
*svn_command
;
118 GError
* error
= NULL
;
120 if (!gtk_builder_add_from_file (bxml
, GLADE_FILE
, &error
))
122 g_warning ("Couldn't load builder file: %s", error
->message
);
123 g_error_free (error
);
126 svn_user_auth
= GTK_WIDGET (gtk_builder_get_object (bxml
, "svn_user_auth"));
127 auth_realm
= GTK_WIDGET (gtk_builder_get_object (bxml
, "auth_realm"));
128 username_entry
= GTK_WIDGET (gtk_builder_get_object (bxml
, "username_entry"));
129 password_entry
= GTK_WIDGET (gtk_builder_get_object (bxml
, "password_entry"));
130 remember_pwd
= GTK_WIDGET (gtk_builder_get_object (bxml
, "remember_pwd"));
132 gtk_dialog_set_default_response (GTK_DIALOG (svn_user_auth
), GTK_RESPONSE_OK
);
135 gtk_label_set_text (GTK_LABEL (auth_realm
), args
->realm
);
138 gtk_entry_set_text (GTK_ENTRY (username_entry
), args
->username
);
139 gtk_widget_grab_focus (password_entry
);
142 gtk_widget_set_sensitive(remember_pwd
, FALSE
);
144 /* Then the dialog is prompted to user and when user clicks ok, the
145 * values entered, i.e username, password and remember password (true
146 * by default) should be used to initialized the memebers below. If the
147 * user cancels the dialog, I think we return an error struct
148 * appropriately initialized. -- naba
151 switch (gtk_dialog_run(GTK_DIALOG(svn_user_auth
)))
153 case GTK_RESPONSE_OK
:
155 *args
->cred
= apr_pcalloc (args
->pool
, sizeof(svn_auth_cred_simple_t
));
156 (*(args
->cred
))->may_save
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
158 (*(args
->cred
))->username
= apr_pstrdup (args
->pool
,
159 gtk_entry_get_text(GTK_ENTRY(username_entry
)));
160 (*(args
->cred
))->password
= apr_pstrdup (args
->pool
,
161 gtk_entry_get_text(GTK_ENTRY(password_entry
)));
167 err
= svn_error_create (SVN_ERR_AUTHN_CREDS_UNAVAILABLE
, NULL
,
168 _("Authentication canceled"));
172 gtk_widget_destroy (svn_user_auth
);
179 ssl_server_trust_prompt (SSLServerTrustArgs
*args
)
181 GtkBuilder
* bxml
= gtk_builder_new ();
182 GtkWidget
* svn_server_trust
;
183 GtkWidget
* auth_realm
;
184 GtkWidget
* server_info
;
185 GtkWidget
* remember_check
;
186 svn_error_t
*err
= NULL
;
188 SvnCommand
*svn_command
;
189 GError
* error
= NULL
;
191 if (!gtk_builder_add_from_file (bxml
, GLADE_FILE
, &error
))
193 g_warning ("Couldn't load builder file: %s", error
->message
);
194 g_error_free (error
);
196 svn_server_trust
= GTK_WIDGET (gtk_builder_get_object (bxml
, "svn_server_trust"));
197 auth_realm
= GTK_WIDGET (gtk_builder_get_object (bxml
, "realm_label"));
198 server_info
= GTK_WIDGET (gtk_builder_get_object (bxml
, "server_info_label"));
199 remember_check
= GTK_WIDGET (gtk_builder_get_object (bxml
, "remember_check"));
202 gtk_label_set_text (GTK_LABEL (auth_realm
), args
->realm
);
204 info
= g_strconcat(_("Hostname:"), args
->cert_info
->hostname
, "\n",
205 _("Fingerprint:"), args
->cert_info
->fingerprint
, "\n",
206 _("Valid from:"), args
->cert_info
->valid_from
, "\n",
207 _("Valid until:"), args
->cert_info
->valid_until
, "\n",
208 _("Issuer DN:"), args
->cert_info
->issuer_dname
, "\n",
209 _("DER certificate:"), args
->cert_info
->ascii_cert
, "\n",
211 gtk_label_set_text (GTK_LABEL (server_info
), info
);
214 gtk_widget_set_sensitive(remember_check
, FALSE
);
216 gtk_dialog_set_default_response (GTK_DIALOG (svn_server_trust
), GTK_RESPONSE_YES
);
219 switch (gtk_dialog_run(GTK_DIALOG(svn_server_trust
)))
221 case GTK_RESPONSE_YES
:
222 *args
->cred
= apr_pcalloc (args
->pool
,
223 sizeof(*(args
->cred
)));
224 (*(args
->cred
))->may_save
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
227 /* TODO: Set bitmask for accepted_failures */
230 err
= svn_error_create (SVN_ERR_AUTHN_CREDS_UNAVAILABLE
, NULL
,
231 _("Authentication canceled"));
234 gtk_widget_destroy (svn_server_trust
);
240 /* User authentication prompts handlers */
242 svn_auth_simple_prompt_func_cb (svn_auth_cred_simple_t
**cred
, void *baton
,
243 const char *realm
, const char *username
,
244 svn_boolean_t may_save
, apr_pool_t
*pool
)
246 SimplePromptArgs
*args
;
247 SvnCommand
*svn_command
;
250 args
= g_new0 (SimplePromptArgs
, 1);
253 args
->realm
= g_strdup (realm
);
254 args
->username
= g_strdup (username
);
255 args
->may_save
= may_save
;
258 svn_command
= SVN_COMMAND (baton
);
260 /* Wait for the dialog to finish */
261 g_mutex_lock (svn_command
->priv
->dialog_finished_lock
);
263 svn_command
->priv
->dialog_finished
= FALSE
;
265 g_idle_add_full (G_PRIORITY_HIGH_IDLE
,
266 (GSourceFunc
) simple_prompt
, args
,
267 (GDestroyNotify
) on_simple_prompt_finished
);
269 while (!svn_command
->priv
->dialog_finished
)
271 g_cond_wait (svn_command
->priv
->dialog_finished_condition
,
272 svn_command
->priv
->dialog_finished_lock
);
276 g_free (args
->realm
);
277 g_free (args
->username
);
280 g_mutex_unlock (svn_command
->priv
->dialog_finished_lock
);
287 svn_auth_ssl_server_trust_prompt_func_cb (svn_auth_cred_ssl_server_trust_t
**cred
,
288 void *baton
, const char *realm
,
289 apr_uint32_t failures
,
290 const svn_auth_ssl_server_cert_info_t
*cert_info
,
291 svn_boolean_t may_save
,
294 SSLServerTrustArgs
*args
;
295 SvnCommand
*svn_command
;
298 args
= g_new0 (SSLServerTrustArgs
, 1);
301 args
->realm
= g_strdup (realm
);
302 args
->failures
= failures
;
303 args
->cert_info
= g_memdup (cert_info
,
304 sizeof (svn_auth_ssl_server_cert_info_t
));
305 args
->may_save
= may_save
;
308 svn_command
= SVN_COMMAND (baton
);
310 /* Wait for the dialog to finish */
311 g_mutex_lock (svn_command
->priv
->dialog_finished_lock
);
313 svn_command
->priv
->dialog_finished
= FALSE
;
315 g_idle_add_full (G_PRIORITY_HIGH_IDLE
,
316 (GSourceFunc
) ssl_server_trust_prompt
, args
,
317 (GDestroyNotify
) on_ssl_server_trust_prompt_finished
);
319 while (!svn_command
->priv
->dialog_finished
)
321 g_cond_wait (svn_command
->priv
->dialog_finished_condition
,
322 svn_command
->priv
->dialog_finished_lock
);
326 g_free (args
->realm
);
327 g_free (args
->cert_info
);
330 g_mutex_unlock (svn_command
->priv
->dialog_finished_lock
);
336 svn_auth_ssl_client_cert_prompt_func_cb (svn_auth_cred_ssl_client_cert_t
**cred
,
337 void *baton
, const char *realm
,
338 svn_boolean_t may_save
,
342 /* Ask for the file where client certificate of authenticity is.
343 * I think it is some sort of private key. */
348 svn_auth_ssl_client_cert_pw_prompt_func_cb (svn_auth_cred_ssl_client_cert_pw_t
**cred
,
349 void *baton
, const char *realm
,
350 svn_boolean_t may_save
,
354 /* Prompt for password only. I think it is pass-phrase of the above key. */
355 return SVN_NO_ERROR
;;
358 /* Notification callback to handle notifications from Subversion itself */
360 on_svn_notify (gpointer baton
,
361 const svn_wc_notify_t
*notify
,
365 gchar
*action_message
;
366 gchar
*state_message
;
368 self
= SVN_COMMAND (baton
);
369 action_message
= NULL
;
370 state_message
= NULL
;
372 switch (notify
->action
)
374 case svn_wc_notify_delete
:
375 action_message
= g_strdup_printf (_("Deleted: %s"), notify
->path
);
377 case svn_wc_notify_add
:
378 action_message
= g_strdup_printf (_("Added: %s"), notify
->path
);
380 case svn_wc_notify_revert
:
381 action_message
= g_strdup_printf ("Reverted: %s", notify
->path
);
383 case svn_wc_notify_failed_revert
:
384 action_message
= g_strdup_printf ("Revert failed: %s",
387 case svn_wc_notify_resolved
:
388 action_message
= g_strdup_printf (_("Resolved: %s"), notify
->path
);
390 case svn_wc_notify_update_delete
:
391 action_message
= g_strdup_printf (_("Deleted: %s"), notify
->path
);
393 case svn_wc_notify_update_update
:
394 action_message
= g_strdup_printf (_("Updated: %s"), notify
->path
);
396 case svn_wc_notify_update_add
:
397 action_message
= g_strdup_printf (_("Added: %s"), notify
->path
);
399 case svn_wc_notify_update_external
:
400 action_message
= g_strdup_printf (_("Externally Updated: %s"),
403 case svn_wc_notify_commit_modified
:
404 action_message
= g_strdup_printf ("Commit Modified: %s",
407 case svn_wc_notify_commit_added
:
408 action_message
= g_strdup_printf ("Commit Added: %s", notify
->path
);
410 case svn_wc_notify_commit_deleted
:
411 action_message
= g_strdup_printf ("Commit Deleted: %s",
414 case svn_wc_notify_commit_replaced
:
415 action_message
= g_strdup_printf ("Commit Replaced: %s",
418 case svn_wc_notify_copy
:
419 action_message
= g_strdup_printf ("Created File: %s", notify
->path
);
427 svn_command_push_info (self
, action_message
);
428 g_free (action_message
);
431 switch (notify
->content_state
)
433 case svn_wc_notify_state_changed
:
434 state_message
= g_strdup_printf (_("Modified: %s"), notify
->path
);
436 case svn_wc_notify_state_merged
:
437 state_message
= g_strdup_printf (_("Merged: %s"), notify
->path
);
439 case svn_wc_notify_state_conflicted
:
440 state_message
= g_strdup_printf (_("Conflicted: %s"),
443 case svn_wc_notify_state_missing
:
444 state_message
= g_strdup_printf (_("Missing: %s"), notify
->path
);
446 case svn_wc_notify_state_obstructed
:
447 state_message
= g_strdup_printf (_("Obstructed: %s"), notify
->path
);
455 svn_command_push_info (self
, state_message
);
456 g_free (state_message
);
460 /* Operation cancelling callback */
462 on_svn_cancel (gpointer cancel_baton
)
466 self
= SVN_COMMAND (cancel_baton
);
468 if (self
->priv
->cancelled
)
469 return svn_error_create (SVN_ERR_CANCELLED
, NULL
, NULL
);
475 svn_command_init (SvnCommand
*self
)
477 svn_auth_baton_t
*auth_baton
;
478 apr_array_header_t
*providers
;
479 svn_auth_provider_object_t
*provider
;
481 self
->priv
= g_new0 (SvnCommandPriv
, 1);
483 self
->priv
->pool
= svn_pool_create (NULL
);
484 svn_client_create_context (&self
->priv
->client_context
, self
->priv
->pool
);
485 self
->priv
->client_context
->notify_func2
= on_svn_notify
;
486 self
->priv
->client_context
->notify_baton2
= self
;
487 self
->priv
->client_context
->cancel_func
= on_svn_cancel
;
488 self
->priv
->client_context
->cancel_baton
= self
;
490 svn_config_get_config (&(self
->priv
->client_context
)->config
,
491 NULL
, /* default dir */
494 self
->priv
->info_messages
= g_queue_new ();
495 self
->priv
->dialog_finished_lock
= g_mutex_new ();
496 self
->priv
->dialog_finished_condition
= g_cond_new ();
498 /* Fill in the auth baton callbacks */
499 providers
= apr_array_make (self
->priv
->pool
, 1,
500 sizeof (svn_auth_provider_object_t
*));
502 /* Provider that authenticates username/password from ~/.subversion */
503 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
504 svn_client_get_simple_provider (&provider
, self
->priv
->pool
);
505 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
507 /* Provider that authenticates server trust from ~/.subversion */
508 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
509 svn_client_get_ssl_server_trust_file_provider (&provider
, self
->priv
->pool
);
510 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
512 /* Provider that authenticates client cert from ~/.subversion */
513 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
514 svn_client_get_ssl_client_cert_file_provider (&provider
, self
->priv
->pool
);
515 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
517 /* Provider that authenticates client cert password from ~/.subversion */
518 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
519 svn_client_get_ssl_client_cert_pw_file_provider (&provider
,
521 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
523 /* Provider that prompts for username/password */
524 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
525 svn_client_get_simple_prompt_provider(&provider
,
526 svn_auth_simple_prompt_func_cb
,
527 self
, 3, self
->priv
->pool
);
528 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
530 /* Provider that prompts for server trust */
531 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
532 svn_client_get_ssl_server_trust_prompt_provider (&provider
,
533 svn_auth_ssl_server_trust_prompt_func_cb
,
536 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
538 /* Provider that prompts for client certificate file */
539 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
540 svn_client_get_ssl_client_cert_prompt_provider (&provider
,
541 svn_auth_ssl_client_cert_prompt_func_cb
,
542 NULL
, 3, self
->priv
->pool
);
543 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
545 /* Provider that prompts for client certificate file password */
546 provider
= apr_pcalloc (self
->priv
->pool
, sizeof(*provider
));
547 svn_client_get_ssl_client_cert_pw_prompt_provider (&provider
,
548 svn_auth_ssl_client_cert_pw_prompt_func_cb
,
551 *(svn_auth_provider_object_t
**)apr_array_push (providers
) = provider
;
553 svn_auth_open (&auth_baton
, providers
, self
->priv
->pool
);
554 self
->priv
->client_context
->auth_baton
= auth_baton
;
559 svn_command_finalize (GObject
*object
)
562 GList
*current_message_line
;
564 self
= SVN_COMMAND (object
);
566 svn_pool_clear (self
->priv
->pool
);
567 svn_pool_destroy (self
->priv
->pool
);
569 current_message_line
= self
->priv
->info_messages
->head
;
571 while (current_message_line
)
573 g_free (current_message_line
->data
);
574 current_message_line
= g_list_next (current_message_line
);
577 g_mutex_free (self
->priv
->dialog_finished_lock
);
578 g_cond_free (self
->priv
->dialog_finished_condition
);
580 g_queue_free (self
->priv
->info_messages
);
583 G_OBJECT_CLASS (svn_command_parent_class
)->finalize (object
);
587 svn_command_cancel (AnjutaCommand
*command
)
591 self
= SVN_COMMAND (command
);
593 self
->priv
->cancelled
= TRUE
;
597 svn_command_class_init (SvnCommandClass
*klass
)
599 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
600 AnjutaCommandClass
*command_class
= ANJUTA_COMMAND_CLASS (klass
);
602 object_class
->finalize
= svn_command_finalize
;
603 command_class
->cancel
= svn_command_cancel
;
608 svn_command_push_info (SvnCommand
*self
, const gchar
*message
)
610 anjuta_async_command_lock (ANJUTA_ASYNC_COMMAND (self
));
611 g_queue_push_tail (self
->priv
->info_messages
, g_strdup (message
));
612 anjuta_async_command_unlock (ANJUTA_ASYNC_COMMAND (self
));
614 anjuta_command_notify_data_arrived (ANJUTA_COMMAND (self
));
618 svn_command_get_info_queue (SvnCommand
*self
)
620 return self
->priv
->info_messages
;
624 svn_command_set_error (SvnCommand
*self
, svn_error_t
*error
)
626 GString
*error_string
;
627 svn_error_t
*current_error
;
628 gchar
*error_c_string
;
630 error_string
= g_string_new ("");
631 current_error
= error
;
633 while (current_error
)
635 g_string_append (error_string
, current_error
->message
);
637 if (current_error
->child
)
638 g_string_append_c (error_string
, '\n');
640 current_error
= current_error
->child
;
643 error_c_string
= g_string_free (error_string
, FALSE
);
644 anjuta_async_command_set_error_message (ANJUTA_COMMAND (self
),
647 g_free (error_c_string
);
651 svn_command_get_client_context (SvnCommand
*self
)
653 return self
->priv
->client_context
;
657 svn_command_get_pool (SvnCommand
*self
)
659 return self
->priv
->pool
;
663 svn_command_make_canonical_path (SvnCommand
*self
, const gchar
*path
)
665 const gchar
*canonical_path
;
667 canonical_path
= NULL
;
670 canonical_path
= svn_path_canonicalize (path
, self
->priv
->pool
);
672 return g_strdup (canonical_path
);
676 svn_command_get_revision (const gchar
*revision
)
678 svn_opt_revision_t
* svn_revision
;
680 svn_revision
= g_new0 (svn_opt_revision_t
, 1);
682 /* FIXME: Parse the revision string */
683 svn_revision
->kind
= svn_opt_revision_head
;
689 svn_command_copy_path_list (GList
*list
)
699 new_list
= g_list_append (new_list
, g_strdup (current_path
->data
));
700 current_path
= g_list_next (current_path
);
707 svn_command_free_path_list (GList
*list
)
715 g_free (current_path
->data
);
716 current_path
= g_list_next (current_path
);