2 * svnput.c : upload a single file to a repository, overwriting
3 * any existing file by the same name.
5 * ***************************************************************
7 * WARNING!! Despite the warnings it gives, this program allows
8 * you to potentially overwrite a file you've never seen.
9 * USE AT YOUR OWN RISK!
11 * (While the repository won't 'lose' overwritten data, the
12 * overwriting may happen without your knowledge, and has the
13 * potential to cause much grief with your collaborators!)
15 * ***************************************************************
17 * ====================================================================
18 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
20 * This software is licensed as described in the file COPYING, which
21 * you should have received as part of this distribution. The terms
22 * are also available at http://subversion.tigris.org/license-1.html.
23 * If newer versions of this license are posted there, you may use a
24 * newer version instead, at your option.
26 * This software consists of voluntary contributions made by many
27 * individuals. For exact contribution history, see the revision
28 * history and logs, available at http://subversion.tigris.org/.
29 * ====================================================================
31 * To compile on unix against Subversion and APR libraries, try
34 * cc svnput.c -o svnput \
35 * -I/usr/local/include/subversion-1 -I/usr/local/apache2/include \
36 * -L/usr/local/apache2/lib -L/usr/local/lib \
37 * -lsvn_client-1 -lapr-0 -laprutil-0
41 #include "svn_client.h"
42 #include "svn_pools.h"
43 #include "svn_config.h"
45 #include "svn_cmdline.h"
50 /* Display a prompt and read a one-line response into the provided buffer,
51 removing a trailing newline if present. */
53 prompt_and_read_line(const char *prompt
,
58 printf("%s: ", prompt
);
59 if (fgets(buffer
, max
, stdin
) == NULL
)
60 return svn_error_create(0, NULL
, "error reading stdin");
62 if (len
> 0 && buffer
[len
-1] == '\n')
67 /* A tiny callback function of type 'svn_auth_simple_prompt_func_t'. For
68 a much better example, see svn_cl__auth_simple_prompt in the official
69 svn cmdline client. */
71 my_simple_prompt_callback (svn_auth_cred_simple_t
**cred
,
75 svn_boolean_t may_save
,
78 svn_auth_cred_simple_t
*ret
= apr_pcalloc (pool
, sizeof (*ret
));
83 printf ("Authentication realm: %s\n", realm
);
87 ret
->username
= apr_pstrdup (pool
, username
);
90 SVN_ERR (prompt_and_read_line("Username", answerbuf
, sizeof(answerbuf
)));
91 ret
->username
= apr_pstrdup (pool
, answerbuf
);
94 SVN_ERR (prompt_and_read_line("Password", answerbuf
, sizeof(answerbuf
)));
95 ret
->password
= apr_pstrdup (pool
, answerbuf
);
102 /* A tiny callback function of type 'svn_auth_username_prompt_func_t'. For
103 a much better example, see svn_cl__auth_username_prompt in the official
104 svn cmdline client. */
106 my_username_prompt_callback (svn_auth_cred_username_t
**cred
,
109 svn_boolean_t may_save
,
112 svn_auth_cred_username_t
*ret
= apr_pcalloc (pool
, sizeof (*ret
));
117 printf ("Authentication realm: %s\n", realm
);
120 SVN_ERR (prompt_and_read_line("Username", answerbuf
, sizeof(answerbuf
)));
121 ret
->username
= apr_pstrdup (pool
, answerbuf
);
127 /* A callback function used when the RA layer needs a handle to a
128 temporary file. This is a reduced version of the callback used in
129 the official svn cmdline client. */
131 open_tmp_file (apr_file_t
**fp
,
132 void *callback_baton
,
136 const char *ignored_filename
;
138 SVN_ERR (svn_io_temp_dir (&path
, pool
));
139 path
= svn_path_join (path
, "tempfile", pool
);
141 /* Open a unique file, with delete-on-close set. */
142 SVN_ERR (svn_io_open_unique_file2 (fp
, &ignored_filename
,
144 svn_io_file_del_on_close
, pool
));
150 /* Called when a commit is successful. */
152 my_commit_callback (svn_revnum_t new_revision
,
157 printf ("Upload complete. Committed revision %ld.\n", new_revision
);
164 main (int argc
, const char **argv
)
169 const char *upload_file
, *URL
;
170 const char *parent_URL
, *basename
;
171 svn_ra_plugin_t
*ra_lib
;
172 void *session
, *ra_baton
;
174 const svn_delta_editor_t
*editor
;
176 svn_dirent_t
*dirent
;
177 svn_ra_callbacks_t
*cbtable
;
178 apr_hash_t
*cfg_hash
;
179 svn_auth_baton_t
*auth_baton
;
183 printf ("Usage: %s PATH URL\n", argv
[0]);
184 printf (" Uploads file at PATH to Subversion repository URL.\n");
187 upload_file
= argv
[1];
190 /* Initialize the app. Send all error messages to 'stderr'. */
191 if (svn_cmdline_init ("minimal_client", stderr
) != EXIT_SUCCESS
)
194 /* Create top-level memory pool. Be sure to read the HACKING file to
195 understand how to properly use/free subpools. */
196 pool
= svn_pool_create (NULL
);
198 /* Initialize the FS library. */
199 err
= svn_fs_initialize (pool
);
200 if (err
) goto hit_error
;
202 /* Make sure the ~/.subversion run-time config files exist, and load. */
203 err
= svn_config_ensure (NULL
, pool
);
204 if (err
) goto hit_error
;
206 err
= svn_config_get_config (&cfg_hash
, NULL
, pool
);
207 if (err
) goto hit_error
;
209 /* Build an authentication baton. */
211 /* There are many different kinds of authentication back-end
212 "providers". See svn_auth.h for a full overview. */
213 svn_auth_provider_object_t
*provider
;
214 apr_array_header_t
*providers
215 = apr_array_make (pool
, 4, sizeof (svn_auth_provider_object_t
*));
217 svn_client_get_simple_prompt_provider (&provider
,
218 my_simple_prompt_callback
,
220 2, /* retry limit */ pool
);
221 APR_ARRAY_PUSH (providers
, svn_auth_provider_object_t
*) = provider
;
223 svn_client_get_username_prompt_provider (&provider
,
224 my_username_prompt_callback
,
226 2, /* retry limit */ pool
);
227 APR_ARRAY_PUSH (providers
, svn_auth_provider_object_t
*) = provider
;
229 /* Register the auth-providers into the context's auth_baton. */
230 svn_auth_open (&auth_baton
, providers
, pool
);
233 /* Create a table of callbacks for the RA session, mostly nonexistent. */
234 cbtable
= apr_pcalloc (pool
, sizeof(*cbtable
));
235 cbtable
->auth_baton
= auth_baton
;
236 cbtable
->open_tmp_file
= open_tmp_file
;
238 /* Now do the real work. */
240 /* Open an RA session to the parent URL, fetch current HEAD rev and
241 "lock" onto that revnum for the remainder of the session. */
242 svn_path_split (URL
, &parent_URL
, &basename
, pool
);
244 err
= svn_ra_init_ra_libs (&ra_baton
, pool
);
245 if (err
) goto hit_error
;
247 err
= svn_ra_get_ra_library (&ra_lib
, ra_baton
, parent_URL
, pool
);
248 if (err
) goto hit_error
;
250 err
= ra_lib
->open (&session
, parent_URL
, cbtable
, NULL
, cfg_hash
, pool
);
251 if (err
) goto hit_error
;
253 err
= ra_lib
->get_latest_revnum (session
, &rev
, pool
);
254 if (err
) goto hit_error
;
256 /* Examine contents of parent dir in the rev. */
257 err
= ra_lib
->get_dir (session
, "", rev
, &dirents
, NULL
, NULL
, pool
);
258 if (err
) goto hit_error
;
260 /* Sanity checks. Don't let the user shoot himself *too* much. */
261 dirent
= apr_hash_get (dirents
, basename
, APR_HASH_KEY_STRING
);
262 if (dirent
&& dirent
->kind
== svn_node_dir
)
264 printf ("Sorry, a directory already exists at that URL.\n");
267 if (dirent
&& dirent
->kind
== svn_node_file
)
271 printf ("\n*** WARNING ***\n\n");
272 printf ("You're about to overwrite r%ld of this file.\n", rev
);
273 printf ("It was last changed by user '%s',\n",
274 dirent
->last_author
? dirent
->last_author
: "?");
275 printf ("on %s.\n", svn_time_to_human_cstring (dirent
->time
, pool
));
276 printf ("\nSomebody *might* have just changed the file seconds ago,\n"
277 "and your upload would be overwriting their changes!\n\n");
279 err
= prompt_and_read_line("Are you SURE you want to upload? [y/n]",
280 answer
, sizeof(answer
));
281 if (err
) goto hit_error
;
283 if (apr_strnatcasecmp (answer
, "y"))
285 printf ("Operation aborted.\n");
290 /* Fetch a commit editor (it's anchored on the parent URL, because
291 the session is too.) */
292 /* ### someday add an option for a user-written commit message? */
293 err
= ra_lib
->get_commit_editor (session
, &editor
, &edit_baton
,
294 "File upload from 'svnput' program.",
295 my_commit_callback
, NULL
, pool
);
296 if (err
) goto hit_error
;
298 /* Drive the editor */
300 void *root_baton
, *file_baton
, *handler_baton
;
301 svn_txdelta_window_handler_t handler
;
302 svn_stream_t
*contents
;
303 apr_file_t
*f
= NULL
;
305 err
= editor
->open_root (edit_baton
, rev
, pool
, &root_baton
);
306 if (err
) goto hit_error
;
310 err
= editor
->add_file (basename
, root_baton
, NULL
, SVN_INVALID_REVNUM
,
315 err
= editor
->open_file (basename
, root_baton
, rev
, pool
,
318 if (err
) goto hit_error
;
320 err
= editor
->apply_textdelta (file_baton
, NULL
, pool
,
321 &handler
, &handler_baton
);
322 if (err
) goto hit_error
;
324 err
= svn_io_file_open (&f
, upload_file
, APR_READ
, APR_OS_DEFAULT
, pool
);
325 if (err
) goto hit_error
;
327 contents
= svn_stream_from_aprfile (f
, pool
);
328 err
= svn_txdelta_send_stream (contents
, handler
, handler_baton
,
330 if (err
) goto hit_error
;
332 err
= svn_io_file_close (f
, pool
);
333 if (err
) goto hit_error
;
335 err
= editor
->close_file (file_baton
, NULL
, pool
);
336 if (err
) goto hit_error
;
338 err
= editor
->close_edit (edit_baton
, pool
);
339 if (err
) goto hit_error
;
345 svn_handle_error2 (err
, stderr
, FALSE
, "svnput: ");