1 /*
2 * minimal_client.c - a minimal Subversion client application ("hello world")
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 *
23 * This app demonstrates how to use the svn_client.h API.
24 *
25 * It reads a directory URL from the commandline, runs
26 * svn_client_list() and prints the list of directory-entries. It
27 * also knows how to deal with basic username/password authentication
28 * challenges.
29 *
30 * For a much more complex example, the svn cmdline client might be
31 * considered the 'reference implementation'.
32 *
33 * From a Linux system, a typical commandline compile might look like:
34 *
35 * cc minimal_client.c -o minimal_client \
36 * -I/usr/local/include/subversion-1 -I/usr/local/apache2/include \
37 * -L/usr/local/apache2/lib -L/usr/local/lib \
38 * -lsvn_client-1 -lapr-0 -laprutil-0
39 *
40 */
41
42
43 #include "svn_client.h"
44 #include "svn_cmdline.h"
45 #include "svn_pools.h"
46 #include "svn_config.h"
47 #include "svn_fs.h"
48
49
50 /* Display a prompt and read a one-line response into the provided buffer,
51 removing a trailing newline if present. */
52 static svn_error_t *
prompt_and_read_line(const char * prompt,char * buffer,size_t max)53 prompt_and_read_line(const char *prompt,
54 char *buffer,
55 size_t max)
56 {
57 int len;
58 printf("%s: ", prompt);
59 if (fgets(buffer, max, stdin) == NULL)
60 return svn_error_create(0, NULL, "error reading stdin");
61 len = strlen(buffer);
62 if (len > 0 && buffer[len-1] == '\n')
63 buffer[len-1] = 0;
64 return SVN_NO_ERROR;
65 }
66
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. */
70 static svn_error_t *
my_simple_prompt_callback(svn_auth_cred_simple_t ** cred,void * baton,const char * realm,const char * username,svn_boolean_t may_save,apr_pool_t * pool)71 my_simple_prompt_callback (svn_auth_cred_simple_t **cred,
72 void *baton,
73 const char *realm,
74 const char *username,
75 svn_boolean_t may_save,
76 apr_pool_t *pool)
77 {
78 svn_auth_cred_simple_t *ret = apr_pcalloc (pool, sizeof (*ret));
79 char answerbuf[100];
80
81 if (realm)
82 {
83 printf ("Authentication realm: %s\n", realm);
84 }
85
86 if (username)
87 ret->username = apr_pstrdup (pool, username);
88 else
89 {
90 SVN_ERR (prompt_and_read_line("Username", answerbuf, sizeof(answerbuf)));
91 ret->username = apr_pstrdup (pool, answerbuf);
92 }
93
94 SVN_ERR (prompt_and_read_line("Password", answerbuf, sizeof(answerbuf)));
95 ret->password = apr_pstrdup (pool, answerbuf);
96
97 *cred = ret;
98 return SVN_NO_ERROR;
99 }
100
101
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. */
105 static svn_error_t *
my_username_prompt_callback(svn_auth_cred_username_t ** cred,void * baton,const char * realm,svn_boolean_t may_save,apr_pool_t * pool)106 my_username_prompt_callback (svn_auth_cred_username_t **cred,
107 void *baton,
108 const char *realm,
109 svn_boolean_t may_save,
110 apr_pool_t *pool)
111 {
112 svn_auth_cred_username_t *ret = apr_pcalloc (pool, sizeof (*ret));
113 char answerbuf[100];
114
115 if (realm)
116 {
117 printf ("Authentication realm: %s\n", realm);
118 }
119
120 SVN_ERR (prompt_and_read_line("Username", answerbuf, sizeof(answerbuf)));
121 ret->username = apr_pstrdup (pool, answerbuf);
122
123 *cred = ret;
124 return SVN_NO_ERROR;
125 }
126
127
128
129 int
main(int argc,const char ** argv)130 main (int argc, const char **argv)
131 {
132 apr_pool_t *pool;
133 svn_error_t *err;
134 svn_opt_revision_t revision;
135 apr_hash_t *dirents;
136 apr_hash_index_t *hi;
137 svn_client_ctx_t *ctx;
138 const char *URL;
139
140 if (argc <= 1)
141 {
142 printf ("Usage: %s URL\n", argv[0]);
143 return EXIT_FAILURE;
144 }
145 else
146 URL = argv[1];
147
148 /* Initialize the app. Send all error messages to 'stderr'. */
149 if (svn_cmdline_init ("minimal_client", stderr) != EXIT_SUCCESS)
150 return EXIT_FAILURE;
151
152 /* Create top-level memory pool. Be sure to read the HACKING file to
153 understand how to properly use/free subpools. */
154 pool = svn_pool_create (NULL);
155
156 /* Initialize the FS library. */
157 err = svn_fs_initialize (pool);
158 if (err)
159 {
160 /* For functions deeper in the stack, we usually use the
161 SVN_ERR() exception-throwing macro (see svn_error.h). At the
162 top level, we catch & print the error with svn_handle_error2(). */
163 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
164 return EXIT_FAILURE;
165 }
166
167 /* Make sure the ~/.subversion run-time config files exist */
168 err = svn_config_ensure (NULL, pool);
169 if (err)
170 {
171 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
172 return EXIT_FAILURE;
173 }
174
175 /* All clients need to fill out a client_ctx object. */
176 {
177 /* Initialize and allocate the client_ctx object. */
178 if ((err = svn_client_create_context (&ctx, pool)))
179 {
180 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
181 return EXIT_FAILURE;
182 }
183
184 /* Load the run-time config file into a hash */
185 if ((err = svn_config_get_config (&(ctx->config), NULL, pool)))
186 {
187 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
188 return EXIT_FAILURE;
189 }
190
191 #ifdef WIN32
192 /* Set the working copy administrative directory name. */
193 if (getenv ("SVN_ASP_DOT_NET_HACK"))
194 {
195 err = svn_wc_set_adm_dir ("_svn", pool);
196 if (err)
197 {
198 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
199 return EXIT_FAILURE;
200 }
201 }
202 #endif
203
204 /* Depending on what your client does, you'll want to read about
205 (and implement) the various callback function types below. */
206
207 /* A func (& context) which receives event signals during
208 checkouts, updates, commits, etc. */
209 /* ctx->notify_func = my_notification_func;
210 ctx->notify_baton = NULL; */
211
212 /* A func (& context) which can receive log messages */
213 /* ctx->log_msg_func = my_log_msg_receiver_func;
214 ctx->log_msg_baton = NULL; */
215
216 /* A func (& context) which checks whether the user cancelled */
217 /* ctx->cancel_func = my_cancel_checking_func;
218 ctx->cancel_baton = NULL; */
219
220 /* Make the client_ctx capable of authenticating users */
221 {
222 /* There are many different kinds of authentication back-end
223 "providers". See svn_auth.h for a full overview.
224
225 If you want to get the auth behavior of the 'svn' program,
226 you can use svn_cmdline_setup_auth_baton, which will give
227 you the exact set of auth providers it uses. This program
228 doesn't use it because it's only appropriate for a command
229 line program, and this is supposed to be a general purpose
230 example. */
231
232 svn_auth_provider_object_t *provider;
233 apr_array_header_t *providers
234 = apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *));
235
236 svn_auth_get_simple_prompt_provider (&provider,
237 my_simple_prompt_callback,
238 NULL, /* baton */
239 2, /* retry limit */ pool);
240 APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
241
242 svn_auth_get_username_prompt_provider (&provider,
243 my_username_prompt_callback,
244 NULL, /* baton */
245 2, /* retry limit */ pool);
246 APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;
247
248 /* Register the auth-providers into the context's auth_baton. */
249 svn_auth_open (&ctx->auth_baton, providers, pool);
250 }
251 } /* end of client_ctx setup */
252
253
254 /* Now do the real work. */
255
256 /* Set revision to always be the HEAD revision. It could, however,
257 be set to a specific revision number, date, or other values. */
258 revision.kind = svn_opt_revision_head;
259
260 /* Main call into libsvn_client does all the work. */
261 err = svn_client_ls (&dirents,
262 URL, &revision,
263 FALSE, /* no recursion */
264 ctx, pool);
265 if (err)
266 {
267 svn_handle_error2 (err, stderr, FALSE, "minimal_client: ");
268 return EXIT_FAILURE;
269 }
270
271 /* Print the dir entries in the hash. */
272 for (hi = apr_hash_first (pool, dirents); hi; hi = apr_hash_next (hi))
273 {
274 const char *entryname;
275 svn_dirent_t *val;
276
277 apr_hash_this (hi, (void *) &entryname, NULL, (void *) &val);
278 printf (" %s\n", entryname);
279
280 /* 'val' is actually an svn_dirent_t structure; a more complex
281 program would mine it for extra printable information. */
282 }
283
284 return EXIT_SUCCESS;
285 }
286