Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / ebrowse.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 2001-2004 Novell, Inc.
4  *
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.
8  *
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.
13  *
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.
18  */
19
20 /* WebDAV test program / utility */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <pthread.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include <libsoup/soup-misc.h>
38
39 #include "e2k-context.h"
40 #include "e2k-restriction.h"
41 #include "e2k-security-descriptor.h"
42 #include "e2k-sid.h"
43 #include "e2k-xml-utils.h"
44
45 #include "e2k-propnames.h"
46 #include "e2k-propnames.c"
47
48 #include "test-utils.h"
49
50 static E2kContext *ctx;
51 static E2kOperation op;
52
53 static const char *folder_tree_props[] = {
54         E2K_PR_DAV_DISPLAY_NAME,
55         E2K_PR_EXCHANGE_FOLDER_CLASS
56 };
57 static const int n_folder_tree_props = sizeof (folder_tree_props) / sizeof (folder_tree_props[0]);
58
59 static void
60 display_folder_tree (E2kContext *ctx, char *top)
61 {
62         E2kRestriction *rn;
63         E2kResultIter *iter;
64         E2kResult *result;
65         int status;
66         const char *name, *class;
67
68         e2k_operation_init (&op);
69         rn = e2k_restriction_prop_bool (E2K_PR_DAV_IS_COLLECTION,
70                                         E2K_RELOP_EQ, TRUE);
71         iter = e2k_context_search_start (ctx, &op, top,
72                                          folder_tree_props,
73                                          n_folder_tree_props,
74                                          rn, NULL, TRUE);
75         e2k_restriction_unref (rn);
76
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);
82
83                 printf ("%s:\n    %s, %s\n", result->href,
84                         name, class ? class : "(No Outlook folder class)");
85         }
86         status = e2k_result_iter_free (iter);
87         e2k_operation_free (&op);
88
89         test_abort_if_http_error (status);
90         test_quit ();
91 }
92
93 static void
94 list_contents (E2kContext *ctx, char *top, gboolean reverse)
95 {
96         E2kRestriction *rn;
97         E2kResultIter *iter;
98         E2kResult *result;
99         const char *prop;
100         int status;
101
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,
107                                          rn, NULL, !reverse);
108         e2k_restriction_unref (rn);
109
110         while ((result = e2k_result_iter_next (iter))) {
111                 printf ("%3d %s (%s)\n", e2k_result_iter_get_index (iter),
112                         result->href,
113                         (char *)e2k_properties_get_prop (result->props,
114                                                          E2K_PR_DAV_DISPLAY_NAME));
115         }
116         status = e2k_result_iter_free (iter);
117         e2k_operation_free (&op);
118
119         test_abort_if_http_error (status);
120         test_quit ();
121 }
122
123 static int
124 mp_compar (const void *k, const void *m)
125 {
126         const char *key = k;
127         struct mapi_proptag *mp = (void *)m;
128
129         return strncmp (key, mp->proptag, 5);
130 }
131
132 static void
133 print_propname (const char *propname)
134 {
135         struct mapi_proptag *mp;
136
137         printf ("  %s", propname);
138
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);
142                 if (mp)
143                         printf (" (%s)", mp->name);
144         }
145
146         printf (":\n");
147 }
148
149 static void
150 print_binary (GByteArray *data)
151 {
152         unsigned char *start, *end, *p;
153
154         end = data->data + data->len;
155         for (start = data->data; start < end; start += 16) {
156                 printf ("    ");
157                 for (p = start; p < end && p < start + 16; p++)
158                         printf ("%02x ", *p);
159                 while (p++ < start + 16)
160                         printf ("   ");
161                 printf ("   ");
162                 for (p = start; p < end && p < start + 16; p++)
163                         printf ("%c", isprint (*p) ? *p : '.');
164                 printf ("\n");
165         }
166 }
167
168 typedef struct {
169         const char *propname;
170         E2kPropType type;
171         gpointer value;
172 } EBrowseProp;
173
174 static int
175 prop_compar (const void *a, const void *b)
176 {
177         EBrowseProp **pa = (void *)a;
178         EBrowseProp **pb = (void *)b;
179
180         return strcmp ((*pa)->propname, (*pb)->propname);
181 }
182
183 static void
184 print_prop (EBrowseProp *prop)
185 {
186         print_propname (prop->propname);
187
188         switch (prop->type) {
189         case E2K_PROP_TYPE_BINARY:
190                 print_binary (prop->value);
191                 break;
192
193         case E2K_PROP_TYPE_STRING_ARRAY:
194         case E2K_PROP_TYPE_INT_ARRAY:
195         {
196                 GPtrArray *array = prop->value;
197                 int i;
198
199                 for (i = 0; i < array->len; i++)
200                         printf ("    %s\n", (char *)array->pdata[i]);
201                 break;
202         }
203
204         case E2K_PROP_TYPE_BINARY_ARRAY:
205         {
206                 GPtrArray *array = prop->value;
207                 int i;
208
209                 for (i = 0; i < array->len; i++) {
210                         print_binary (array->pdata[i]);
211                         printf ("\n");
212                 }
213                 break;
214         }
215
216         case E2K_PROP_TYPE_XML:
217                 printf ("    (xml)\n");
218                 break;
219
220         case E2K_PROP_TYPE_STRING:
221         default:
222                 printf ("    %s\n", (char *)prop->value);
223                 break;
224         }
225 }
226
227 static void
228 add_prop (const char *propname, E2kPropType type, gpointer value, gpointer props)
229 {
230         EBrowseProp *prop;
231
232         prop = g_new0 (EBrowseProp, 1);
233         prop->propname = propname;
234         prop->type = type;
235         prop->value = value;
236         g_ptr_array_add (props, prop);
237 }
238
239 static void
240 print_properties (E2kResult *results, int nresults)
241 {
242         GPtrArray *props;
243         int i;
244
245         if (nresults != 1) {
246                 printf ("Got %d results?\n", nresults);
247                 test_quit ();
248                 return;
249         }
250
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); 
255
256         for (i = 0; i < props->len; i++)
257                 print_prop (props->pdata[i]);
258
259         test_quit ();
260 }
261
262 static void
263 got_all_properties (SoupMessage *msg, gpointer ctx)
264 {
265         E2kResult *results;
266         int nresults;
267
268         test_abort_if_http_error (msg->status_code);
269
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);
274 }
275
276
277 #define ALL_PROPS \
278 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" \
279 "<propfind xmlns=\"DAV:\" xmlns:e=\"http://schemas.microsoft.com/exchange/\">" \
280 "  <allprop>" \
281 "    <e:allprop/>" \
282 "  </allprop>" \
283 "</propfind>"
284
285 static void
286 get_all_properties (E2kContext *ctx, char *uri)
287 {
288         SoupMessage *msg;
289
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");
295
296         e2k_context_queue_message (ctx, msg, got_all_properties, ctx);
297 }
298
299 static void
300 get_property (E2kContext *ctx, char *uri, char *prop)
301 {
302         E2kHTTPStatus status;
303         E2kResult *results;
304         int nresults, i;
305
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,
311                                                     NULL);
312                                 break;
313                         }
314         }
315
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);
324 }
325
326 static void
327 get_fav_properties(E2kContext *ctx, char *uri)
328 {
329         E2kRestriction *rn;
330         E2kResultIter *iter;
331         E2kResult *result;
332         const char *prop;
333         int status;
334         char *eml_str, *top = uri, fav_uri[1024];
335
336         
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,
343                                          rn, NULL, FALSE);
344         e2k_restriction_unref (rn);
345
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");
350
351                 strcat(fav_uri, eml_str);
352
353                 printf("\nNAME:\n");
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");
370
371                 memset(fav_uri, 0, 1024);
372         }
373         status = e2k_result_iter_free (iter);
374         e2k_operation_free (&op);
375
376         test_abort_if_http_error (status);
377         test_quit ();
378 }
379
380 static void
381 get_sd (E2kContext *ctx, char *uri)
382 {
383         const char *props[] = {
384                 E2K_PR_EXCHANGE_SD_BINARY,
385                 E2K_PR_EXCHANGE_SD_XML,
386         };
387         E2kHTTPStatus status;
388         E2kResult *results;
389         int nresults;
390         xmlNodePtr xml_form;
391         GByteArray *binary_form;
392         E2kSecurityDescriptor *sd;
393         E2kPermissionsRole role;
394         guint32 perms;
395         GList *sids, *s;
396         E2kSid *sid;
397
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);
403
404         if (nresults == 0)
405                 goto done;
406
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)
412                 goto done;
413
414         xmlElemDump (stdout, NULL, xml_form);
415         printf ("\n");
416
417         print_binary (binary_form);
418         printf ("\n");
419
420         sd = e2k_security_descriptor_new (xml_form, binary_form);
421         if (!sd) {
422                 printf ("(Could not parse)\n");
423                 goto done;
424         }
425
426         sids = e2k_security_descriptor_get_sids (sd);
427         for (s = sids; s; s = s->next) {
428                 sid = s->data;
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);
435         }
436         g_list_free (sids);
437
438         if (!e2k_security_descriptor_to_binary (sd))
439                 printf ("\nSD is malformed.\n");
440         g_object_unref (sd);
441
442  done:
443         test_quit ();
444 }
445
446 static void
447 get_body (E2kContext *ctx, char *uri)
448 {
449         E2kHTTPStatus status;
450         char *body;
451         int len;
452
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);
457
458         fwrite (body, 1, len, stdout);
459         test_quit ();
460 }
461
462 static void
463 delete (E2kContext *ctx, char *uri)
464 {
465         E2kHTTPStatus status;
466
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);
471         test_quit ();
472 }
473
474 static void
475 notify (E2kContext *ctx, const char *uri,
476         E2kContextChangeType type, gpointer user_data)
477 {
478         switch (type) {
479         case E2K_CONTEXT_OBJECT_CHANGED:
480                 printf ("Changed\n");
481                 break;
482         case E2K_CONTEXT_OBJECT_ADDED:
483                 printf ("Added\n");
484                 break;
485         case E2K_CONTEXT_OBJECT_REMOVED:
486                 printf ("Removed\n");
487                 break;
488         case E2K_CONTEXT_OBJECT_MOVED:
489                 printf ("Moved\n");
490                 break;
491         }
492 }
493
494 static void
495 subscribe (E2kContext *ctx, char *uri)
496 {
497         e2k_context_subscribe (ctx, uri,
498                                E2K_CONTEXT_OBJECT_CHANGED, 0,
499                                notify, NULL);
500         e2k_context_subscribe (ctx, uri,
501                                E2K_CONTEXT_OBJECT_ADDED, 0,
502                                notify, NULL);
503         e2k_context_subscribe (ctx, uri,
504                                E2K_CONTEXT_OBJECT_REMOVED, 0,
505                                notify, NULL);
506         e2k_context_subscribe (ctx, uri,
507                                E2K_CONTEXT_OBJECT_MOVED, 0,
508                                notify, NULL);
509 }
510
511 static void
512 move (E2kContext *ctx, char *from, char *to, gboolean delete)
513 {
514         GPtrArray *source_hrefs;
515         E2kResultIter *iter;
516         E2kResult *result;
517         E2kHTTPStatus status;
518
519         source_hrefs = g_ptr_array_new ();
520         g_ptr_array_add (source_hrefs, "");
521
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);
526
527         result = e2k_result_iter_next (iter);
528         if (result) {
529                 if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (result->status))
530                         printf ("Failed: %d\n", result->status);
531                 else {
532                         printf ("moved to %s\n",
533                                 (char *)e2k_properties_get_prop (result->props,
534                                                                  E2K_PR_DAV_LOCATION));
535                 }
536         }
537         status = e2k_result_iter_free (iter);
538         e2k_operation_free (&op);
539
540         test_abort_if_http_error (status);
541         test_quit ();
542 }
543
544 static void
545 name (E2kContext *ctx, char *alias, char *uri_prefix)
546 {
547         E2kHTTPStatus status;
548         char *uri, *body;
549         int len;
550         xmlDoc *doc;
551         xmlNode *item, *node;
552         char *data;
553
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);
559
560         doc = e2k_parse_xml (body, len);
561
562         if ((node = e2k_xml_find (doc->children, "error")))
563                 printf ("Error: %s\n", xmlNodeGetContent (node));
564         else {
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);
570                                         if (data && *data)
571                                                 printf ("%s: %s\n", node->name, data);
572                                         xmlFree (data);
573                                 }
574                         }
575                 }
576         }
577
578         xmlFreeDoc (doc);
579         test_quit ();
580 }
581
582 static void
583 put (E2kContext *ctx, const char *file, const char *uri)
584 {
585         struct stat st;
586         char *buf;
587         int fd;
588         E2kHTTPStatus status;
589
590         fd = open (file, O_RDONLY);
591         if (fd == -1 || fstat (fd, &st) == -1) {
592                 fprintf (stderr, "%s\n", strerror (errno));
593                 exit (1);
594         }
595         buf = g_malloc (st.st_size);
596         read (fd, buf, st.st_size);
597         close (fd);
598
599         e2k_operation_init (&op);
600         status = e2k_context_put (ctx, &op, uri,
601                                   "message/rfc822", buf, st.st_size,
602                                   NULL);
603         e2k_operation_free (&op);
604         test_abort_if_http_error (status);
605         test_quit ();
606 }
607
608 static void *
609 cancel (void *op)
610 {
611         e2k_operation_cancel (op);
612         return NULL;
613 }
614
615 static void
616 quit (int sig)
617 {
618         static pthread_t cancel_thread;
619
620         /* Can't cancel from here because we might be
621          * inside a malloc.
622          */
623         if (!cancel_thread) {
624                 pthread_create (&cancel_thread, NULL, cancel, &op);
625         } else
626                 exit (0);
627 }
628
629 static void
630 usage (void)
631 {
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");
643         exit (1);
644 }
645
646 const char *test_program_name = "ebrowse";
647
648 void
649 test_main (int argc, char **argv)
650 {
651         char *uri;
652
653         signal (SIGINT, quit);
654
655         uri = argv[argc - 1];
656         ctx = test_get_context (uri);
657
658         switch (argv[1][1]) {
659         case 't':
660                 display_folder_tree (ctx, uri);
661                 break;
662
663         case 'l':
664                 list_contents (ctx, uri, FALSE);
665                 break;
666
667         case 'L':
668                 list_contents (ctx, uri, TRUE);
669                 break;
670
671         case 'b':
672                 get_body (ctx, uri);
673                 break;
674
675         case 'd':
676                 delete (ctx, uri);
677                 break;
678
679         case 'p':
680                 get_all_properties (ctx, uri);
681                 break;
682
683         case 'P':
684                 get_property (ctx, uri, argv[2]);
685                 break;
686
687         case 'S':
688                 get_sd (ctx, uri);
689                 break;
690
691         case 's':
692                 subscribe (ctx, uri);
693                 break;
694
695         case 'm':
696         case 'c':
697                 move (ctx, argv[2], uri, argv[1][1] == 'm');
698                 break;
699
700         case 'n':
701                 name (ctx, argv[2], uri);
702                 break;
703
704         case 'q':
705                 put (ctx, argv[2], uri);
706                 break;
707
708         case 'f':
709                 get_fav_properties(ctx, uri);
710                 break;
711
712         default:
713                 usage ();
714         }
715 }