1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Copyright (C) 2001-2004 Novell, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /* WebDAV test program / utility */
37 #include <libsoup/soup-misc.h>
39 #include "e2k-context.h"
40 #include "e2k-restriction.h"
41 #include "e2k-security-descriptor.h"
43 #include "e2k-xml-utils.h"
45 #include "e2k-propnames.h"
46 #include "e2k-propnames.c"
48 #include "test-utils.h"
50 static E2kContext *ctx;
51 static E2kOperation op;
53 static const char *folder_tree_props[] = {
54 E2K_PR_DAV_DISPLAY_NAME,
55 E2K_PR_EXCHANGE_FOLDER_CLASS
57 static const int n_folder_tree_props = sizeof (folder_tree_props) / sizeof (folder_tree_props[0]);
60 display_folder_tree (E2kContext *ctx, char *top)
66 const char *name, *class;
68 e2k_operation_init (&op);
69 rn = e2k_restriction_prop_bool (E2K_PR_DAV_IS_COLLECTION,
71 iter = e2k_context_search_start (ctx, &op, top,
75 e2k_restriction_unref (rn);
77 while ((result = e2k_result_iter_next (iter))) {
78 name = e2k_properties_get_prop (result->props,
79 E2K_PR_DAV_DISPLAY_NAME);
80 class = e2k_properties_get_prop (result->props,
81 E2K_PR_EXCHANGE_FOLDER_CLASS);
83 printf ("%s:\n %s, %s\n", result->href,
84 name, class ? class : "(No Outlook folder class)");
86 status = e2k_result_iter_free (iter);
87 e2k_operation_free (&op);
89 test_abort_if_http_error (status);
94 list_contents (E2kContext *ctx, char *top, gboolean reverse)
102 e2k_operation_init (&op);
103 prop = E2K_PR_DAV_DISPLAY_NAME;
104 rn = e2k_restriction_prop_bool (E2K_PR_DAV_IS_COLLECTION,
105 E2K_RELOP_EQ, FALSE);
106 iter = e2k_context_search_start (ctx, &op, top, &prop, 1,
108 e2k_restriction_unref (rn);
110 while ((result = e2k_result_iter_next (iter))) {
111 printf ("%3d %s (%s)\n", e2k_result_iter_get_index (iter),
113 (char *)e2k_properties_get_prop (result->props,
114 E2K_PR_DAV_DISPLAY_NAME));
116 status = e2k_result_iter_free (iter);
117 e2k_operation_free (&op);
119 test_abort_if_http_error (status);
124 mp_compar (const void *k, const void *m)
127 struct mapi_proptag *mp = (void *)m;
129 return strncmp (key, mp->proptag, 5);
133 print_propname (const char *propname)
135 struct mapi_proptag *mp;
137 printf (" %s", propname);
139 if (!strncmp (propname, E2K_NS_MAPI_PROPTAG, sizeof (E2K_NS_MAPI_PROPTAG) - 1)) {
140 mp = bsearch (propname + 42, mapi_proptags, nmapi_proptags,
141 sizeof (struct mapi_proptag), mp_compar);
143 printf (" (%s)", mp->name);
150 print_binary (GByteArray *data)
152 unsigned char *start, *end, *p;
154 end = data->data + data->len;
155 for (start = data->data; start < end; start += 16) {
157 for (p = start; p < end && p < start + 16; p++)
158 printf ("%02x ", *p);
159 while (p++ < start + 16)
162 for (p = start; p < end && p < start + 16; p++)
163 printf ("%c", isprint (*p) ? *p : '.');
169 const char *propname;
175 prop_compar (const void *a, const void *b)
177 EBrowseProp **pa = (void *)a;
178 EBrowseProp **pb = (void *)b;
180 return strcmp ((*pa)->propname, (*pb)->propname);
184 print_prop (EBrowseProp *prop)
186 print_propname (prop->propname);
188 switch (prop->type) {
189 case E2K_PROP_TYPE_BINARY:
190 print_binary (prop->value);
193 case E2K_PROP_TYPE_STRING_ARRAY:
194 case E2K_PROP_TYPE_INT_ARRAY:
196 GPtrArray *array = prop->value;
199 for (i = 0; i < array->len; i++)
200 printf (" %s\n", (char *)array->pdata[i]);
204 case E2K_PROP_TYPE_BINARY_ARRAY:
206 GPtrArray *array = prop->value;
209 for (i = 0; i < array->len; i++) {
210 print_binary (array->pdata[i]);
216 case E2K_PROP_TYPE_XML:
220 case E2K_PROP_TYPE_STRING:
222 printf (" %s\n", (char *)prop->value);
228 add_prop (const char *propname, E2kPropType type, gpointer value, gpointer props)
232 prop = g_new0 (EBrowseProp, 1);
233 prop->propname = propname;
236 g_ptr_array_add (props, prop);
240 print_properties (E2kResult *results, int nresults)
246 printf ("Got %d results?\n", nresults);
251 printf ("%s\n", results[0].href);
252 props = g_ptr_array_new ();
253 e2k_properties_foreach (results[0].props, add_prop, props);
254 qsort (props->pdata, props->len, sizeof (gpointer), prop_compar);
256 for (i = 0; i < props->len; i++)
257 print_prop (props->pdata[i]);
263 got_all_properties (SoupMessage *msg, gpointer ctx)
268 test_abort_if_http_error (msg->status_code);
270 e2k_results_from_multistatus (msg, &results, &nresults);
271 test_abort_if_http_error (msg->status_code);
272 print_properties (results, nresults);
273 e2k_results_free (results, nresults);
278 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" \
279 "<propfind xmlns=\"DAV:\" xmlns:e=\"http://schemas.microsoft.com/exchange/\">" \
286 get_all_properties (E2kContext *ctx, char *uri)
290 msg = e2k_soup_message_new_full (ctx, uri, "PROPFIND",
291 "text/xml", SOUP_BUFFER_USER_OWNED,
292 ALL_PROPS, strlen (ALL_PROPS));
293 soup_message_add_header (msg->request_headers, "Brief", "t");
294 soup_message_add_header (msg->request_headers, "Depth", "0");
296 e2k_context_queue_message (ctx, msg, got_all_properties, ctx);
300 get_property (E2kContext *ctx, char *uri, char *prop)
302 E2kHTTPStatus status;
306 if (!strncmp (prop, "PR_", 3)) {
307 for (i = 0; i < nmapi_proptags; i++)
308 if (!strcmp (mapi_proptags[i].name, prop)) {
309 prop = g_strconcat (E2K_NS_MAPI_PROPTAG,
310 mapi_proptags[i].proptag,
316 e2k_operation_init (&op);
317 status = e2k_context_propfind (ctx, &op, uri,
318 (const char **)&prop, 1,
319 &results, &nresults);
320 e2k_operation_free (&op);
321 test_abort_if_http_error (status);
322 print_properties (results, nresults);
323 e2k_results_free (results, nresults);
327 get_fav_properties(E2kContext *ctx, char *uri)
334 char *eml_str, *top = uri, fav_uri[1024];
337 /* list the contents and search for the favorite properties */
338 e2k_operation_init (&op);
339 prop = E2K_PR_DAV_DISPLAY_NAME;
340 rn = e2k_restriction_prop_bool (E2K_PR_DAV_IS_COLLECTION,
341 E2K_RELOP_EQ, FALSE);
342 iter = e2k_context_search_start (ctx, &op, top, &prop, 1,
344 e2k_restriction_unref (rn);
346 while ((result = e2k_result_iter_next (iter))) {
347 strcpy(fav_uri, uri);
348 eml_str = strstr(result->href, "Shortcuts");
349 eml_str = eml_str + strlen("Shortcuts");
351 strcat(fav_uri, eml_str);
354 get_property (ctx, fav_uri, PR_FAV_DISPLAY_NAME);
355 printf("\nALIAS:\n");
356 get_property (ctx, fav_uri, PR_FAV_DISPLAY_ALIAS);
357 printf("\nPUBLIC SOURCE KEY:\n");
358 get_property (ctx, fav_uri, PR_FAV_PUBLIC_SOURCE_KEY);
359 printf("\nPARENT SOURCE KEY:\n");
360 get_property (ctx, fav_uri, PR_FAV_PARENT_SOURCE_KEY);
361 printf("\nAUTO SUBFOLDERS:\n");
362 get_property (ctx, fav_uri, PR_FAV_AUTOSUBFOLDERS);
363 printf("\nLEVEL MASK:\n");
364 get_property (ctx, fav_uri, PR_FAV_LEVEL_MASK);
365 printf("\nINHERIT AUTO:\n");
366 get_property (ctx, fav_uri, PR_FAV_INHERIT_AUTO);
367 printf("\nDEL SUBS:\n");
368 get_property (ctx, fav_uri, PR_FAV_DEL_SUBS);
369 printf("\n\t\t=================================================\n");
371 memset(fav_uri, 0, 1024);
373 status = e2k_result_iter_free (iter);
374 e2k_operation_free (&op);
376 test_abort_if_http_error (status);
381 get_sd (E2kContext *ctx, char *uri)
383 const char *props[] = {
384 E2K_PR_EXCHANGE_SD_BINARY,
385 E2K_PR_EXCHANGE_SD_XML,
387 E2kHTTPStatus status;
391 GByteArray *binary_form;
392 E2kSecurityDescriptor *sd;
393 E2kPermissionsRole role;
398 e2k_operation_init (&op);
399 status = e2k_context_propfind (ctx, &op, uri, props, 2,
400 &results, &nresults);
401 e2k_operation_free (&op);
402 test_abort_if_http_error (status);
407 xml_form = e2k_properties_get_prop (results[0].props,
408 E2K_PR_EXCHANGE_SD_XML);
409 binary_form = e2k_properties_get_prop (results[0].props,
410 E2K_PR_EXCHANGE_SD_BINARY);
411 if (!xml_form || !binary_form)
414 xmlElemDump (stdout, NULL, xml_form);
417 print_binary (binary_form);
420 sd = e2k_security_descriptor_new (xml_form, binary_form);
422 printf ("(Could not parse)\n");
426 sids = e2k_security_descriptor_get_sids (sd);
427 for (s = sids; s; s = s->next) {
429 perms = e2k_security_descriptor_get_permissions (sd, sid);
430 role = e2k_permissions_role_find (perms);
431 printf ("%s: %s (0x%lx)\n",
432 e2k_sid_get_display_name (sid),
433 e2k_permissions_role_get_name (role),
434 (unsigned long)perms);
438 if (!e2k_security_descriptor_to_binary (sd))
439 printf ("\nSD is malformed.\n");
447 get_body (E2kContext *ctx, char *uri)
449 E2kHTTPStatus status;
453 e2k_operation_init (&op);
454 status = e2k_context_get (ctx, &op, uri, NULL, &body, &len);
455 e2k_operation_free (&op);
456 test_abort_if_http_error (status);
458 fwrite (body, 1, len, stdout);
463 delete (E2kContext *ctx, char *uri)
465 E2kHTTPStatus status;
467 e2k_operation_init (&op);
468 status = e2k_context_delete (ctx, &op, uri);
469 e2k_operation_free (&op);
470 test_abort_if_http_error (status);
475 notify (E2kContext *ctx, const char *uri,
476 E2kContextChangeType type, gpointer user_data)
479 case E2K_CONTEXT_OBJECT_CHANGED:
480 printf ("Changed\n");
482 case E2K_CONTEXT_OBJECT_ADDED:
485 case E2K_CONTEXT_OBJECT_REMOVED:
486 printf ("Removed\n");
488 case E2K_CONTEXT_OBJECT_MOVED:
495 subscribe (E2kContext *ctx, char *uri)
497 e2k_context_subscribe (ctx, uri,
498 E2K_CONTEXT_OBJECT_CHANGED, 0,
500 e2k_context_subscribe (ctx, uri,
501 E2K_CONTEXT_OBJECT_ADDED, 0,
503 e2k_context_subscribe (ctx, uri,
504 E2K_CONTEXT_OBJECT_REMOVED, 0,
506 e2k_context_subscribe (ctx, uri,
507 E2K_CONTEXT_OBJECT_MOVED, 0,
512 move (E2kContext *ctx, char *from, char *to, gboolean delete)
514 GPtrArray *source_hrefs;
517 E2kHTTPStatus status;
519 source_hrefs = g_ptr_array_new ();
520 g_ptr_array_add (source_hrefs, "");
522 e2k_operation_init (&op);
523 iter = e2k_context_transfer_start (ctx, &op, from, to,
524 source_hrefs, delete);
525 g_ptr_array_free (source_hrefs, TRUE);
527 result = e2k_result_iter_next (iter);
529 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (result->status))
530 printf ("Failed: %d\n", result->status);
532 printf ("moved to %s\n",
533 (char *)e2k_properties_get_prop (result->props,
534 E2K_PR_DAV_LOCATION));
537 status = e2k_result_iter_free (iter);
538 e2k_operation_free (&op);
540 test_abort_if_http_error (status);
545 name (E2kContext *ctx, char *alias, char *uri_prefix)
547 E2kHTTPStatus status;
551 xmlNode *item, *node;
554 uri = g_strdup_printf ("%s?Cmd=galfind&AN=%s", uri_prefix, alias);
555 e2k_operation_init (&op);
556 status = e2k_context_get_owa (ctx, &op, uri, TRUE, &body, &len);
557 e2k_operation_free (&op);
558 test_abort_if_http_error (status);
560 doc = e2k_parse_xml (body, len);
562 if ((node = e2k_xml_find (doc->children, "error")))
563 printf ("Error: %s\n", xmlNodeGetContent (node));
565 item = doc->children;
566 while ((item = e2k_xml_find (item, "item"))) {
567 for (node = item->children; node; node = node->next) {
568 if (node->type == XML_ELEMENT_NODE) {
569 data = xmlNodeGetContent (node);
571 printf ("%s: %s\n", node->name, data);
583 put (E2kContext *ctx, const char *file, const char *uri)
588 E2kHTTPStatus status;
590 fd = open (file, O_RDONLY);
591 if (fd == -1 || fstat (fd, &st) == -1) {
592 fprintf (stderr, "%s\n", strerror (errno));
595 buf = g_malloc (st.st_size);
596 read (fd, buf, st.st_size);
599 e2k_operation_init (&op);
600 status = e2k_context_put (ctx, &op, uri,
601 "message/rfc822", buf, st.st_size,
603 e2k_operation_free (&op);
604 test_abort_if_http_error (status);
611 e2k_operation_cancel (op);
618 static pthread_t cancel_thread;
620 /* Can't cancel from here because we might be
623 if (!cancel_thread) {
624 pthread_create (&cancel_thread, NULL, cancel, &op);
632 printf ("usage: ebrowse -t URI (shallow folder tree)\n");
633 printf (" ebrowse [-l | -L ] URI (contents listing [back/forward])\n");
634 printf (" ebrowse [ -p | -P prop ] URI (look up all/one prop)\n");
635 printf (" ebrowse -S URI (look up security descriptor)\n");
636 printf (" ebrowse -b URI (fetch body)\n");
637 printf (" ebrowse -q FILE URI (put body)\n");
638 printf (" ebrowse -d URI (delete)\n");
639 printf (" ebrowse -s URI (subscribe and listen)\n");
640 printf (" ebrowse [ -m | -c ] SRC DEST (move/copy)\n");
641 printf (" ebrowse -n ALIAS URI (lookup name)\n");
642 printf (" ebrowse -f URI (lookup favorite folder props)\n");
646 const char *test_program_name = "ebrowse";
649 test_main (int argc, char **argv)
653 signal (SIGINT, quit);
655 uri = argv[argc - 1];
656 ctx = test_get_context (uri);
658 switch (argv[1][1]) {
660 display_folder_tree (ctx, uri);
664 list_contents (ctx, uri, FALSE);
668 list_contents (ctx, uri, TRUE);
680 get_all_properties (ctx, uri);
684 get_property (ctx, uri, argv[2]);
692 subscribe (ctx, uri);
697 move (ctx, argv[2], uri, argv[1][1] == 'm');
701 name (ctx, argv[2], uri);
705 put (ctx, argv[2], uri);
709 get_fav_properties(ctx, uri);