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