~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201210021442

« back to all changes in this revision

Viewing changes to services/plugins/unity/ghIntegration/platform.cc

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*********************************************************
 
2
 * Copyright (C) 2008-2010 VMware, Inc. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License as published
 
6
 * by the Free Software Foundation version 2.1 and no later version.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
10
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 
11
 * License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 
15
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 
16
 *
 
17
 *********************************************************/
 
18
 
 
19
 
 
20
/*
 
21
 * ghIntegrationX11.c --
 
22
 *
 
23
 *    Guest-host integration implementation for POSIX-compliant platforms that run X11.
 
24
 *
 
25
 *    The main tasks done by this code are reading in the system's .desktop files to turn
 
26
 *    them into an internal representation of available applications on the system
 
27
 *    (implemented by GHIPlatformReadAllApplications, GHIPlatformReadApplicationsDir,
 
28
 *    GHIPlatformReadDesktopFile, and kin), and feeding portions of that internal
 
29
 *    representation to the host upon request
 
30
 *    (GHIPlatform{OpenStartMenuTree,GetStartMenuItem,CloseStartMenuTree}).
 
31
 */
 
32
 
 
33
 
 
34
#include <stdio.h>
 
35
#include <limits.h>
 
36
#include <sys/param.h>
 
37
#include <sys/types.h>
 
38
#include <sys/stat.h>
 
39
#include <sys/wait.h>
 
40
#include <unistd.h>
 
41
#include <libgen.h>
 
42
 
 
43
#ifndef GTK2
 
44
#error "Gtk 2.0 is required"
 
45
#endif
 
46
 
 
47
#include <gtk/gtk.h>
 
48
#include <gtkmm.h>
 
49
#include <glibmm.h>
 
50
#include <giomm.h>
 
51
#include <glib.h>
 
52
#include <gdk-pixbuf/gdk-pixbuf-core.h>
 
53
#include <sigc++/sigc++.h>
 
54
 
 
55
// gdkx.h includes Xlib.h, which #defines Bool.
 
56
#include <gdk/gdkx.h>
 
57
#undef Bool
 
58
 
 
59
#include <gio/gdesktopappinfo.h>
 
60
 
 
61
extern "C" {
 
62
#include "vmware.h"
 
63
#include "vmware/tools/guestrpc.h"
 
64
#include "base64.h"
 
65
#include "dbllnklst.h"
 
66
#include "debug.h"
 
67
#include "util.h"
 
68
#include "region.h"
 
69
#include "unity.h"
 
70
#include "unityCommon.h"
 
71
#include "system.h"
 
72
#include "codeset.h"
 
73
#include "imageUtil.h"
 
74
#include "str.h"
 
75
#include "strutil.h"
 
76
#include <paths.h>
 
77
#include "vm_atomic.h"
 
78
#include "mntinfo.h"
 
79
#include "guest_msg_def.h"
 
80
#include "Uri.h"
 
81
#include "xdg.h"
 
82
};
 
83
 
 
84
#define URI_TEXTRANGE_EQUAL(textrange, str) \
 
85
   (((textrange).afterLast - (textrange).first) == (ssize_t) strlen((str))        \
 
86
    && !strncmp((textrange).first, (str), (textrange).afterLast - (textrange).first))
 
87
 
 
88
#include "appUtil.h"
 
89
#include "ghIntegration.h"
 
90
#include "ghIntegrationInt.h"
 
91
#include "ghiX11icon.h"
 
92
 
 
93
#ifdef REDIST_GMENU
 
94
#   include "vmware/tools/ghi/menuItemManager.hh"
 
95
using vmware::tools::ghi::MenuItemManager;
 
96
using vmware::tools::ghi::MenuItem;
 
97
#endif
 
98
 
 
99
#include "vmware/tools/ghi/pseudoAppMgr.hh"
 
100
using vmware::tools::ghi::PseudoAppMgr;
 
101
using vmware::tools::ghi::PseudoApp;
 
102
 
 
103
 
 
104
using vmware::tools::NotifyIconCallback;
 
105
 
 
106
/*
 
107
 * These describe possible start menu item flags. It should come from ghiCommon.h
 
108
 * eventually.
 
109
 */
 
110
#define UNITY_START_MENU_ITEM_DIRECTORY (1 << 0)
 
111
 
 
112
/*
 
113
 * This macro provides an estimate of how much space an icon might take beyond the actual
 
114
 * icon data when returned from unity.get.binary.info. This makes space for the
 
115
 * width/height/size strings, and adds enough padding to give some breathing room just in
 
116
 * case.
 
117
 *
 
118
 * > This is only an estimate. <
 
119
 */
 
120
#define ICON_SPACE_PADDING (sizeof "999x999x65535x" + 25)
 
121
 
 
122
 
 
123
/*
 
124
 * GHI/X11 context object
 
125
 */
 
126
 
 
127
struct _GHIPlatform {
 
128
   GTree *apps; // Tree of GHIMenuDirectory's, keyed & ordered by their dirname
 
129
   GHashTable *appsByExecutable; // Translates full executable path to GHIMenuItem
 
130
   GHashTable *appsByDesktopEntry; // Translates full .desktop path to GHIMenuItem
 
131
   /*
 
132
    * Translates arbitrary executable paths as discovered through
 
133
    * UnityPlatformGetWindowPaths to a .desktop-ful executable URI.
 
134
    *
 
135
    * Example:
 
136
    * (key)   /usr/lib/firefox-3.6.3/firefox-bin (via Firefox window's _NET_WM_PID)
 
137
    * (value) file:///usr/bin/firefox?DesktopEntry=/usr/share/applications/firefox.desktop
 
138
    */
 
139
   GHashTable *appsByWindowExecutable;
 
140
 
 
141
   /* Pre-wrapper script environment.  See @ref System_GetNativeEnviron. */
 
142
   std::vector<Glib::ustring> nativeEnviron;
 
143
 
 
144
   /* Callbacks to send data (RPCs) to the host */
 
145
   GHIHostCallbacks hostCallbacks;
 
146
 
 
147
#ifdef REDIST_GMENU
 
148
   /* Launch menu item layout generator thing. */
 
149
   MenuItemManager *menuItemManager;
 
150
#endif
 
151
};
 
152
 
 
153
/*
 
154
 * The GHIMenuItem object represents an individual leaf-node menu item (corresponding to
 
155
 * a .desktop file).
 
156
 */
 
157
typedef struct {
 
158
   char *exepath;     // The full exe path for use in GHIPlatform::appsByExecutable
 
159
   char *keyfilePath; // Key to GHIPlatform::appsByDesktopEntry, used in %k field code
 
160
   GKeyFile *keyfile; // glib data structure representing the parsed .desktop file
 
161
} GHIMenuItem;
 
162
 
 
163
/*
 
164
 * Represents a "start menu folder" so to speak.
 
165
 */
 
166
typedef struct {
 
167
   const char *dirname;         // The .desktop category that this object represents
 
168
   const char *prettyDirname;   // (optional) A prettier version of dirname.
 
169
   GPtrArray *items;            // Array of pointers to GHIMenuItems
 
170
} GHIMenuDirectory;
 
171
 
 
172
/*
 
173
 * Represents an active handle for traversing a menu.
 
174
 */
 
175
typedef struct {
 
176
   int handleID;
 
177
   enum { LAUNCH_FOLDER, FIXED_FOLDER, DIRECTORY_FOLDER } handleType;
 
178
   GHIMenuDirectory *gmd; // Only set for DIRECTORY_FOLDER handles
 
179
} GHIMenuHandle;
 
180
 
 
181
/*
 
182
 * This is used to help us find the Nth GHIMenuDirectory node in the GHIPlatform::apps
 
183
 * tree, an operation that is needed as part of GHIPlatformGetStartMenuItem...
 
184
 */
 
185
typedef struct {
 
186
   int currentItem;
 
187
   int desiredItem;
 
188
   GHIMenuDirectory *gmd; // OUT - pointer to the Nth GHIMenuDirectory
 
189
} GHITreeTraversal;
 
190
 
 
191
static char *GHIPlatformUriPathToString(UriPathSegmentA *path);
 
192
 
 
193
 
 
194
/*
 
195
 * GHI capabilities for this platform.
 
196
 */
 
197
/*
 
198
 * XXX TODO: re-enable once ShellAction is implemented.
 
199
 */
 
200
/*
 
201
static GuestCapabilities platformGHICaps[] = {
 
202
   GHI_CAP_CMD_SHELL_ACTION,
 
203
   GHI_CAP_SHELL_ACTION_BROWSE,
 
204
   GHI_CAP_SHELL_ACTION_RUN,
 
205
   GHI_CAP_SHELL_LOCATION_HGFS
 
206
};
 
207
*/
 
208
 
 
209
#if !defined(OPEN_VM_TOOLS)
 
210
/*
 
211
 * An empty file type list - a reference to this can be returned by
 
212
 * GHIPlatformGetBinaryHandlers() in some circumstances.
 
213
 */
 
214
static FileTypeList sEmptyFileTypeList;
 
215
#endif // OPEN_VM_TOOLS
 
216
 
 
217
 
 
218
static bool AppInfoLaunchEnv(GHIPlatform* ghip, GAppInfo* appInfo);
 
219
static void OnMenusChanged(GHIPlatform* ghip);
 
220
 
 
221
 
 
222
/*
 
223
 *----------------------------------------------------------------------------
 
224
 *
 
225
 * GHIPlatformIsSupported --
 
226
 *
 
227
 *      Determine whether this guest supports guest host integration.
 
228
 *
 
229
 * Results:
 
230
 *      TRUE if the guest supports GHI
 
231
 *      FALSE otherwise
 
232
 *
 
233
 * Side effects:
 
234
 *      None
 
235
 *
 
236
 *----------------------------------------------------------------------------
 
237
 */
 
238
 
 
239
Bool
 
240
GHIPlatformIsSupported(void)
 
241
{
 
242
   const char *desktopEnv = Xdg_DetectDesktopEnv();
 
243
   Bool supported = (g_strcmp0(desktopEnv, "GNOME") == 0) ||
 
244
                    (g_strcmp0(desktopEnv, "KDE") == 0);
 
245
   if (!supported) {
 
246
      g_message("GHI not available under unsupported desktop environment %s\n",
 
247
                desktopEnv ? desktopEnv : "(nil)");
 
248
   }
 
249
   return supported;
 
250
}
 
251
 
 
252
 
 
253
/*
 
254
 *----------------------------------------------------------------------------
 
255
 *
 
256
 * GHIPlatformInit --
 
257
 *
 
258
 *      Sets up the platform-specific GHI state.
 
259
 *
 
260
 * Results:
 
261
 *      Pointer to platform-specific data (may be NULL).
 
262
 *
 
263
 * Side effects:
 
264
 *      None
 
265
 *
 
266
 *----------------------------------------------------------------------------
 
267
 */
 
268
 
 
269
GHIPlatform *
 
270
GHIPlatformInit(GMainLoop *mainLoop,            // IN
 
271
                const char **envp,              // IN
 
272
                GHIHostCallbacks hostCallbacks) // IN
 
273
{
 
274
   GHIPlatform *ghip;
 
275
   const char *desktopEnv;
 
276
 
 
277
   Gtk::Main::init_gtkmm_internals();
 
278
 
 
279
   if (!GHIPlatformIsSupported()) {
 
280
      /*
 
281
       * Don't bother allocating resources if running under an unsupported
 
282
       * desktop environment.
 
283
       */
 
284
      return NULL;
 
285
   }
 
286
 
 
287
   ghip = (GHIPlatform *) Util_SafeCalloc(1, sizeof *ghip);
 
288
   ghip->appsByWindowExecutable =
 
289
      g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 
290
   ghip->hostCallbacks = hostCallbacks;
 
291
   AppUtil_Init();
 
292
 
 
293
   const char** tmp;
 
294
   for (tmp = envp; *tmp; tmp++) {
 
295
      ghip->nativeEnviron.push_back(*tmp);
 
296
   }
 
297
 
 
298
   desktopEnv = Xdg_DetectDesktopEnv();
 
299
   ASSERT(desktopEnv); // Asserting based on GHIPlatformIsSupported check above.
 
300
   g_desktop_app_info_set_desktop_env(desktopEnv);
 
301
 
 
302
#ifdef REDIST_GMENU
 
303
   ghip->menuItemManager = new MenuItemManager(desktopEnv);
 
304
   sigc::slot<void,GHIPlatform*> menuSlot = sigc::ptr_fun(&OnMenusChanged);
 
305
   ghip->menuItemManager->menusChanged.connect(sigc::bind(menuSlot, ghip));
 
306
   OnMenusChanged(ghip);
 
307
#endif
 
308
 
 
309
   return ghip;
 
310
}
 
311
 
 
312
 
 
313
/*
 
314
 *----------------------------------------------------------------------------
 
315
 *
 
316
 * GHIPlatformRegisterCaps --
 
317
 *
 
318
 *      Register guest platform specific capabilities with the VMX.
 
319
 *
 
320
 * Results:
 
321
 *      None.
 
322
 *
 
323
 * Side effects:
 
324
 *      None.
 
325
 *
 
326
 *----------------------------------------------------------------------------
 
327
 */
 
328
 
 
329
void
 
330
GHIPlatformRegisterCaps(GHIPlatform *ghip) // IN
 
331
{
 
332
   ASSERT(ghip);
 
333
   //ASSERT(platformGHICaps);
 
334
 
 
335
   /*
 
336
    * XXX TODO: re-enable once ShellAction is implemented.
 
337
    */
 
338
   //AppUtil_SendGuestCaps(platformGHICaps, ARRAYSIZE(platformGHICaps), TRUE);
 
339
}
 
340
 
 
341
 
 
342
/*
 
343
 *----------------------------------------------------------------------------
 
344
 *
 
345
 * GHIPlatformUnregisterCaps --
 
346
 *
 
347
 *      Register guest platform specific capabilities with the VMX.
 
348
 *
 
349
 * Results:
 
350
 *      None.
 
351
 *
 
352
 * Side effects:
 
353
 *      None.
 
354
 *
 
355
 *----------------------------------------------------------------------------
 
356
 */
 
357
 
 
358
void
 
359
GHIPlatformUnregisterCaps(GHIPlatform *ghip) // IN
 
360
{
 
361
   ASSERT(ghip);
 
362
   //ASSERT(platformGHICaps);
 
363
 
 
364
   /*
 
365
    * XXX TODO: re-enable once ShellAction is implemented.
 
366
    */
 
367
   //AppUtil_SendGuestCaps(platformGHICaps, ARRAYSIZE(platformGHICaps), FALSE);
 
368
}
 
369
 
 
370
 
 
371
/*
 
372
 *----------------------------------------------------------------------------
 
373
 *
 
374
 * GHIPlatformCleanup --
 
375
 *
 
376
 *      Tears down the platform-specific GHI state.
 
377
 *
 
378
 * Results:
 
379
 *      None.
 
380
 *
 
381
 * Side effects:
 
382
 *      GHIPlatform is no longer valid.
 
383
 *
 
384
 *----------------------------------------------------------------------------
 
385
 */
 
386
 
 
387
void
 
388
GHIPlatformCleanup(GHIPlatform *ghip) // IN
 
389
{
 
390
   if (!ghip) {
 
391
      return;
 
392
   }
 
393
 
 
394
#ifdef REDIST_GMENU
 
395
   delete ghip->menuItemManager;
 
396
#endif
 
397
   g_hash_table_destroy(ghip->appsByWindowExecutable);
 
398
   free(ghip);
 
399
}
 
400
 
 
401
 
 
402
/*
 
403
 *-----------------------------------------------------------------------------
 
404
 *
 
405
 * GHIPlatformRegisterNotifyIconCallback / GHIPlatformUnregisterNotifyIconCallback --
 
406
 *
 
407
 *      Register/Unregister the NotifyIcon Callback object. Since notification icons
 
408
 *      (aka Tray icons) are unsupported on Linux guests this function does nothing.
 
409
 *
 
410
 * Results:
 
411
 *      None.
 
412
 *
 
413
 * Side effects:
 
414
 *      Adds data into the DynBuf.
 
415
 *
 
416
 *-----------------------------------------------------------------------------
 
417
 */
 
418
 
 
419
void
 
420
GHIPlatformRegisterNotifyIconCallback(NotifyIconCallback *notifyIconCallback) // IN
 
421
{
 
422
}
 
423
 
 
424
void GHIPlatformUnregisterNotifyIconCallback(NotifyIconCallback *notifyIconCallback)   // IN
 
425
{
 
426
}
 
427
 
 
428
 
 
429
/*
 
430
 *----------------------------------------------------------------------------
 
431
 *
 
432
 * GHIPlatformGetBinaryInfo --
 
433
 *
 
434
 *      Get binary information (app name and icons). We're passed app info in
 
435
 *      pathURIUtf8 (in URI format), and we find the app info by looking up the
 
436
 *      path in GHIPlatform->appsByExecutable. Once we find it, we can retrieve
 
437
 *      info on the app from the .desktop file.
 
438
 *
 
439
 * Results:
 
440
 *      TRUE if everything went ok, FALSE otherwise.
 
441
 *
 
442
 * Side effects:
 
443
 *      None
 
444
 *
 
445
 *----------------------------------------------------------------------------
 
446
 */
 
447
 
 
448
Bool
 
449
GHIPlatformGetBinaryInfo(GHIPlatform *ghip,         // IN: platform-specific state
 
450
                         const char *pathURIUtf8,   // IN: full path to the binary file
 
451
                         std::string &friendlyName,               // OUT: Friendly name
 
452
                         std::list<GHIBinaryIconInfo> &iconList)  // OUT: Icons
 
453
{
 
454
   const char *realCmd = NULL;
 
455
#if 0
 
456
   char *keyfilePath = NULL;
 
457
   unsigned long windowID = 0;
 
458
   gpointer freeMe = NULL;
 
459
#endif
 
460
   UriParserStateA state;
 
461
   UriUriA uri;
 
462
 
 
463
   ASSERT(ghip);
 
464
   ASSERT(pathURIUtf8);
 
465
 
 
466
   memset(&state, 0, sizeof state);
 
467
   memset(&uri, 0, sizeof uri);
 
468
   state.uri = &uri;
 
469
 
 
470
   /* Strip query component. */
 
471
   size_t uriSize = strlen(pathURIUtf8) + 1;
 
472
   gchar *uriSansQuery = (gchar*)g_alloca(uriSize);
 
473
   memcpy(uriSansQuery, pathURIUtf8, uriSize);
 
474
   gchar *tmp = strchr(uriSansQuery, '?');
 
475
   if (tmp) { *tmp = '\0'; }
 
476
 
 
477
   if (uriSansQuery[0] == '/') {
 
478
      realCmd = uriSansQuery;
 
479
   } else if (uriParseUriA(&state, uriSansQuery) == URI_SUCCESS) {
 
480
      if (URI_TEXTRANGE_EQUAL(uri.scheme, "file")) {
 
481
         gchar* tmp = (gchar*)g_alloca(strlen(uriSansQuery) + 1);
 
482
         uriUriStringToUnixFilenameA(uriSansQuery, tmp);
 
483
 
 
484
         Glib::ustring unixFile;
 
485
         unixFile.assign(tmp);
 
486
 
 
487
         Glib::ustring contentType;
 
488
         bool uncertain;
 
489
         contentType = Gio::content_type_guess(unixFile, std::string(""),
 
490
                                               uncertain);
 
491
 
 
492
         Bool success = FALSE;
 
493
         PseudoAppMgr appMgr;
 
494
         PseudoApp app;
 
495
 
 
496
         /*
 
497
          * H'okay.  So we're looking up icons, yeah?
 
498
          *
 
499
          * 1.  If given a URI for an XDG desktop entry file, search for an icon based
 
500
          *     on its Icon key.
 
501
          * 2.  If given a pseudo app URI, as identified by appMgr, use the special
 
502
          *     icon associated with said pseudo app.
 
503
          * 3.  If given a folder, try going with "folder" (per icon-naming-spec).
 
504
          * 4.  Else fall back to searching our theme for an icon based on MIME/
 
505
          *     content type.
 
506
          */
 
507
 
 
508
         if (g_str_has_suffix(unixFile.c_str(), ".desktop")) {
 
509
            Glib::RefPtr<Gio::DesktopAppInfo> desktopFileInfo;
 
510
 
 
511
            desktopFileInfo = Gio::DesktopAppInfo::create_from_filename(unixFile);
 
512
            if (desktopFileInfo) {
 
513
               friendlyName = desktopFileInfo->get_name();
 
514
               GHIX11IconGetIconsForDesktopFile(unixFile.c_str(), iconList);
 
515
               success = TRUE;
 
516
            }
 
517
         } else if (appMgr.GetAppByUri(uriSansQuery, app)) {
 
518
            friendlyName = app.symbolicName;
 
519
            GHIX11IconGetIconsByName(app.iconName.c_str(), iconList);
 
520
            success = TRUE;
 
521
         } else if (Glib::file_test(unixFile, Glib::FILE_TEST_IS_DIR)) {
 
522
            friendlyName = Glib::filename_display_basename(unixFile);
 
523
            GHIX11IconGetIconsByName("folder", iconList);
 
524
            success = TRUE;
 
525
         } else {
 
526
            friendlyName = Glib::filename_display_basename(unixFile);
 
527
            size_t i = 0;
 
528
            while ((i = contentType.find('/', i)) != contentType.npos) {
 
529
               contentType.replace(i, 1, "-");
 
530
            }
 
531
            GHIX11IconGetIconsByName(contentType.c_str(), iconList);
 
532
            success = TRUE;
 
533
         }
 
534
 
 
535
         uriFreeUriMembersA(&uri);
 
536
         return success;
 
537
#if 0
 
538
         UriQueryListA *queryList = NULL;
 
539
         int itemCount;
 
540
 
 
541
         freeMe = GHIPlatformUriPathToString(uri.pathHead);
 
542
         realCmd = (const char *) freeMe;
 
543
         if (g_str_has_suffix(realCmd, ".desktop") &&
 
544
             (desktopFileInfo = g_desktop_app_info_new_from_filename(realCmd))
 
545
              != NULL) {
 
546
            Bool success;
 
547
 
 
548
            friendlyName = g_app_info_get_name(G_APP_INFO(desktopFileInfo));
 
549
            g_object_unref(desktopFileInfo);
 
550
 
 
551
            success = GHIX11IconGetIconsForDesktopFile(realCmd, iconList);
 
552
            uriFreeUriMembersA(&uri);
 
553
            return success;
 
554
         } else if (uriDissectQueryMallocA(&queryList, &itemCount,
 
555
                                           uri.query.first,
 
556
                                           uri.query.afterLast) == URI_SUCCESS) {
 
557
            UriQueryListA *cur;
 
558
 
 
559
            for (cur = queryList; cur; cur = cur->next) {
 
560
               if (!cur->value) {
 
561
                  continue;
 
562
               }
 
563
 
 
564
               if (strcmp(cur->key, "WindowXID") == 0) {
 
565
                  sscanf(cur->value, "%lu", &windowID); // Ignore any failures
 
566
               } else if (strcmp(cur->key, "DesktopEntry") == 0) {
 
567
                  keyfilePath = g_strdup(cur->value);
 
568
               }
 
569
            }
 
570
 
 
571
            uriFreeQueryListA(queryList);
 
572
         }
 
573
#endif
 
574
      } else {
 
575
         uriFreeUriMembersA(&uri);
 
576
         Debug("Binary URI %s does not have a 'file' scheme\n", pathURIUtf8);
 
577
         return FALSE;
 
578
      }
 
579
   } else {
 
580
      uriFreeUriMembersA(&uri);
 
581
      return FALSE;
 
582
   }
 
583
 
 
584
   return FALSE;
 
585
 
 
586
#if 0
 
587
   /*
 
588
    * If for some reason the command we got wasn't a fullly expanded filesystem path,
 
589
    * then expand the command into a full path.
 
590
    */
 
591
   if (realCmd[0] != '/') {
 
592
      ctmp = g_find_program_in_path(realCmd);
 
593
      if (ctmp && *ctmp) {
 
594
         free(freeMe);
 
595
         freeMe = ctmp;
 
596
         realCmd = ctmp;
 
597
      } else {
 
598
         free(ctmp);
 
599
         free(freeMe);
 
600
         return FALSE;
 
601
      }
 
602
   }
 
603
 
 
604
   if (keyfilePath) {
 
605
      ghm = (GHIMenuItem *) g_hash_table_lookup(ghip->appsByDesktopEntry, keyfilePath);
 
606
      g_free(keyfilePath);
 
607
   }
 
608
 
 
609
   if (!ghm) {
 
610
      /*
 
611
       * Now that we have the full path, look it up in our hash table of GHIMenuItems
 
612
       */
 
613
      ghm = (GHIMenuItem *) g_hash_table_lookup(ghip->appsByExecutable, realCmd);
 
614
   }
 
615
 
 
616
   if (!ghm) {
 
617
      /*
 
618
       * To deal with /usr/bin/gimp being a symlink to gimp-2.x, also try symlinks.
 
619
       */
 
620
      char newPath[PATH_MAX + 1];
 
621
      ssize_t linkLen;
 
622
 
 
623
      linkLen = readlink(realCmd, newPath, sizeof newPath - 1);
 
624
      if (linkLen > 0) {
 
625
         char *slashLoc;
 
626
 
 
627
         newPath[linkLen] = '\0';
 
628
         slashLoc = strrchr(realCmd, '/');
 
629
         if (newPath[0] != '/' && slashLoc) {
 
630
            ctmp = g_strdup_printf("%.*s%s",
 
631
                                   (int)((slashLoc + 1) - realCmd),
 
632
                                   realCmd, newPath);
 
633
            g_free(freeMe);
 
634
            freeMe = ctmp;
 
635
            realCmd = (const char *) freeMe;
 
636
         } else {
 
637
            realCmd = newPath;
 
638
         }
 
639
 
 
640
         ghm = (GHIMenuItem *) g_hash_table_lookup(ghip->appsByExecutable, realCmd);
 
641
      }
 
642
   }
 
643
   /*
 
644
    * Stick the app name into 'friendlyName'.
 
645
    */
 
646
   if (ghm) {
 
647
      ctmp = g_key_file_get_locale_string(ghm->keyfile, G_KEY_FILE_DESKTOP_GROUP,
 
648
                                   G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
 
649
      if (!ctmp) {
 
650
         ctmp = g_path_get_basename(realCmd);
 
651
      }
 
652
      friendlyName = ctmp;
 
653
      free(ctmp);
 
654
   } else {
 
655
      /*
 
656
       * If we can't find it, then just tell the host that the app name is the same as
 
657
       * the basename of the application's path.
 
658
       */
 
659
      ctmp = strrchr(realCmd, '/');
 
660
      if (ctmp) {
 
661
         ctmp++;
 
662
      } else {
 
663
         ctmp = (char *) realCmd;
 
664
      }
 
665
      friendlyName = ctmp;
 
666
   }
 
667
 
 
668
   free(freeMe);
 
669
   ctmp = NULL;
 
670
   freeMe = NULL;
 
671
 
 
672
   GHIPlatformCollectIconInfo(ghip, ghm, windowID, iconList);
 
673
 
 
674
#endif
 
675
   return TRUE;
 
676
}
 
677
 
 
678
 
 
679
#if !defined(OPEN_VM_TOOLS)
 
680
/*
 
681
 *----------------------------------------------------------------------------
 
682
 *
 
683
 * GHIPlatformGetBinaryHandlers --
 
684
 *
 
685
 *      Get the list of filetypes and URL protocols supported by a binary
 
686
 *      (application). We're passed an app path in URI format, and we find
 
687
 *      the app info by looking up the path in GHIPlatform->appsByExecutable.
 
688
 *      Once we find it, we can retrieve info on the app from the .desktop file.
 
689
 *
 
690
 * Results:
 
691
 *      A Filetype list of the handlers.
 
692
 *
 
693
 * Side effects:
 
694
 *      None
 
695
 *
 
696
 *----------------------------------------------------------------------------
 
697
 */
 
698
 
 
699
const FileTypeList&
 
700
GHIPlatformGetBinaryHandlers(GHIPlatform *ghip,      // IN: platform-specific state
 
701
                             const char *pathUtf8)   // IN: full path to the executable
 
702
{
 
703
   return sEmptyFileTypeList;
 
704
}
 
705
#endif // OPEN_VM_TOOLS
 
706
 
 
707
 
 
708
/*
 
709
 *----------------------------------------------------------------------------
 
710
 *
 
711
 * GHIPlatformOpenStartMenuTree --
 
712
 *
 
713
 *      Get start menu item count for a given root. This function should be
 
714
 *      called before iterating through the menu item subtree.
 
715
 *      To start at the root of the start menu, pass in "" for the root.
 
716
 *
 
717
 *      The output 'buf' is a string holding two numbers separated by a space:
 
718
 *          1. A handle ID for this menu tree iterator.
 
719
 *          2. A count of the items in this iterator.
 
720
 *
 
721
 * Results:
 
722
 *      TRUE if we were able to get the count successfully
 
723
 *      FALSE otherwise
 
724
 *
 
725
 * Side effects:
 
726
 *      None
 
727
 *
 
728
 *----------------------------------------------------------------------------
 
729
 */
 
730
 
 
731
Bool
 
732
GHIPlatformOpenStartMenuTree(GHIPlatform *ghip,        // IN: platform-specific state
 
733
                             const char *rootUtf8,     // IN: root of the tree
 
734
                             uint32 flags,             // IN: flags
 
735
                             DynBuf *buf)              // OUT: number of items
 
736
{
 
737
   Bool success = FALSE;
 
738
 
 
739
#ifdef REDIST_GMENU
 
740
   std::pair<uint32,uint32> descriptor;
 
741
   if (ghip->menuItemManager->OpenMenuTree(rootUtf8, &descriptor)) {
 
742
      char tmp[2 * sizeof MAKESTR(UINT_MAX)];
 
743
      Str_Sprintf(tmp, sizeof tmp, "%u %u", descriptor.first, descriptor.second);
 
744
      DynBuf_AppendString(buf, tmp);
 
745
      success = TRUE;
 
746
   }
 
747
#endif
 
748
 
 
749
   return success;
 
750
}
 
751
 
 
752
 
 
753
/*
 
754
 *-----------------------------------------------------------------------------
 
755
 *
 
756
 * GHIPlatformMenuItemToURI --
 
757
 *
 
758
 *      Returns the URI that would be used to launch a particular GHI menu item
 
759
 *
 
760
 * Results:
 
761
 *      Newly allocated URI string
 
762
 *
 
763
 * Side effects:
 
764
 *      Allocates memory for the URI.
 
765
 *
 
766
 *-----------------------------------------------------------------------------
 
767
 */
 
768
 
 
769
static char *
 
770
GHIPlatformMenuItemToURI(GHIPlatform *ghip, // IN
 
771
                         GHIMenuItem *gmi)  // IN
 
772
{
 
773
   gchar **argv;
 
774
   gint argc;
 
775
 
 
776
   char *ctmp;
 
777
   UriQueryListA *queryItems;
 
778
   int i;
 
779
   int err;
 
780
   gboolean res;
 
781
   int nchars;
 
782
   char *uriString;
 
783
   char *queryString;
 
784
 
 
785
   ASSERT(ghip);
 
786
   ASSERT(gmi);
 
787
 
 
788
   ctmp = g_key_file_get_string(gmi->keyfile, G_KEY_FILE_DESKTOP_GROUP,
 
789
                                G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
 
790
 
 
791
   res = g_shell_parse_argv(ctmp, &argc, &argv, NULL);
 
792
   g_free(ctmp);
 
793
   if (!res) {
 
794
      return NULL;
 
795
   }
 
796
 
 
797
   queryItems = (UriQueryListA *) alloca((argc + 1) * sizeof *queryItems);
 
798
 
 
799
   for (i = 0; i < (argc - 1); i++) {
 
800
      queryItems[i].key = "argv[]";
 
801
      queryItems[i].value = argv[i + 1];
 
802
      queryItems[i].next = &queryItems[i + 1];
 
803
   }
 
804
   queryItems[i].key = "DesktopEntry";
 
805
   queryItems[i].value = gmi->keyfilePath;
 
806
   queryItems[i].next = NULL;
 
807
 
 
808
   /*
 
809
    * 10 + 3 * len is the formula recommended by uriparser for the maximum URI string
 
810
    * length.
 
811
    */
 
812
   uriString = (char *) alloca(10 + 3 * strlen(gmi->exepath));
 
813
   if (uriUnixFilenameToUriStringA(gmi->exepath, uriString)) {
 
814
      g_strfreev(argv);
 
815
      return NULL;
 
816
   }
 
817
   if (uriComposeQueryCharsRequiredA(queryItems, &nchars) != URI_SUCCESS) {
 
818
      g_strfreev(argv);
 
819
      return NULL;
 
820
   }
 
821
   queryString = (char *) alloca(nchars + 1);
 
822
   err = uriComposeQueryA(queryString, queryItems, nchars + 1, &i);
 
823
   g_strfreev(argv);
 
824
   if (err != URI_SUCCESS) {
 
825
      return NULL;
 
826
   }
 
827
 
 
828
   return g_strdup_printf("%s?%s", uriString, queryString);
 
829
}
 
830
 
 
831
 
 
832
/*
 
833
 *----------------------------------------------------------------------------
 
834
 *
 
835
 * GHIPlatformGetStartMenuItem --
 
836
 *
 
837
 *      Get start menu item at a given index. This function should be called
 
838
 *      in the loop to get all items for a menu sub-tree.
 
839
 *      If there are no more items, the function will return FALSE.
 
840
 *
 
841
 *      Upon returning, 'buf' will hold a nul-delimited array of strings:
 
842
 *         1. User-visible item name.
 
843
 *         2. UNITY_START_MENU_ITEM_* flag.
 
844
 *         3. Executable path.
 
845
 *         4. Localized user-visible item name.
 
846
 *
 
847
 * Results:
 
848
 *      TRUE if there's an item at a given index, FALSE otherwise.
 
849
 *
 
850
 * Side effects:
 
851
 *      None
 
852
 *
 
853
 *----------------------------------------------------------------------------
 
854
 */
 
855
 
 
856
Bool
 
857
GHIPlatformGetStartMenuItem(GHIPlatform *ghip, // IN: platform-specific state
 
858
                            uint32 handle,     // IN: tree handle
 
859
                            uint32 itemIndex,  // IN: the index of the item in the tree
 
860
                            DynBuf *buf)       // OUT: item
 
861
{
 
862
   Bool success = FALSE;
 
863
 
 
864
#ifdef REDIST_GMENU
 
865
   const MenuItem* menuItem;
 
866
   const Glib::ustring* path;
 
867
 
 
868
   if (ghip->menuItemManager->GetMenuItem(handle, itemIndex, &menuItem, &path)) {
 
869
      Glib::ustring key = *path + "/" + menuItem->key;
 
870
      DynBuf_AppendString(buf, key.c_str());
 
871
 
 
872
      char tmp[sizeof MAKESTR(UINT_MAX)];
 
873
      Str_Sprintf(tmp, sizeof tmp, "%u", menuItem->isFolder ? 1 : 0);
 
874
      DynBuf_AppendString(buf, tmp);
 
875
 
 
876
      DynBuf_AppendString(buf, menuItem->execPath.c_str());
 
877
      DynBuf_AppendString(buf, menuItem->displayName.c_str());
 
878
      success = TRUE;
 
879
   }
 
880
#endif
 
881
 
 
882
   return success;
 
883
}
 
884
 
 
885
 
 
886
/*
 
887
 *----------------------------------------------------------------------------
 
888
 *
 
889
 * GHIPlatformCloseStartMenu --
 
890
 *
 
891
 *      Free all memory associated with this start menu tree and cleanup.
 
892
 *
 
893
 * Results:
 
894
 *      TRUE if the handle is valid
 
895
 *      FALSE otherwise
 
896
 *
 
897
 * Side effects:
 
898
 *      None
 
899
 *
 
900
 *----------------------------------------------------------------------------
 
901
 */
 
902
 
 
903
Bool
 
904
GHIPlatformCloseStartMenuTree(GHIPlatform *ghip, // IN: platform-specific state
 
905
                              uint32 handle)     // IN: handle to the tree to be closed
 
906
{
 
907
#ifdef REDIST_GMENU
 
908
   return ghip->menuItemManager->CloseMenuTree(handle);
 
909
#else
 
910
   return FALSE;
 
911
#endif
 
912
}
 
913
 
 
914
 
 
915
#if 0 // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
916
/*
 
917
 *-----------------------------------------------------------------------------
 
918
 *
 
919
 * GHIPlatformFindHGFSShare --
 
920
 *
 
921
 *      Finds the filesystem path to a particular HGFS sharename
 
922
 *
 
923
 * Results:
 
924
 *      Newly heap-allocated path to the top of the specified share.
 
925
 *
 
926
 * Side effects:
 
927
 *      Allocates memory for the return value.
 
928
 *
 
929
 *-----------------------------------------------------------------------------
 
930
 */
 
931
 
 
932
static char *
 
933
GHIPlatformFindHGFSShare(GHIPlatform *ghip,              // IN
 
934
                         const UriTextRangeA *sharename) // IN
 
935
{
 
936
   FILE *fh;
 
937
   struct mntent *ment;
 
938
 
 
939
   fh = Posix_Setmntent(_PATH_MOUNTED, "r");
 
940
   if (!fh) {
 
941
      return NULL;
 
942
   }
 
943
 
 
944
   while ((ment = Posix_Getmntent(fh))) {
 
945
      char *fsSharename;
 
946
      if (strcmp(ment->mnt_type, "hgfs") && strcmp(ment->mnt_type, "vmhgfs")) {
 
947
         continue;
 
948
      }
 
949
 
 
950
      if (!StrUtil_StartsWith(ment->mnt_fsname, ".host:")) {
 
951
         Warning("HGFS filesystem has an fsname of \"%s\" rather than \".host:...\"\n",
 
952
                 ment->mnt_fsname);
 
953
         continue;
 
954
      }
 
955
 
 
956
      if (ment->mnt_fsname[strlen(".host:")] == '/') {
 
957
         fsSharename = ment->mnt_fsname + strlen(".host:/");
 
958
      } else {
 
959
         fsSharename = ment->mnt_fsname + strlen(".host:");
 
960
      }
 
961
 
 
962
      /*
 
963
       * XXX this function's logic could be improved substantially to do deeper matching
 
964
       * (e.g. if someone has .host:/foo/bar mounted, but nothing else, and is looking to
 
965
       * open the document share://foo/bar/baz). Don't know if HGFS allows that, but
 
966
       * that'd require passing in the whole URI rather than just the sharename.
 
967
       */
 
968
      if (URI_TEXTRANGE_EQUAL(*sharename, fsSharename)) {
 
969
         char *retval = g_strdup(ment->mnt_dir);
 
970
 
 
971
         fclose(fh);
 
972
 
 
973
         return retval;
 
974
      } else if (fsSharename == '\0') {
 
975
         /*
 
976
          * This is a mount of the toplevel HGFS directory, so we know it should work.
 
977
          */
 
978
         char *retval = g_strdup_printf("%s/%.*s",
 
979
                                        ment->mnt_dir,
 
980
                                        (int)(sharename->afterLast - sharename->first),
 
981
                                        sharename->first);
 
982
         fclose(fh);
 
983
         return retval;
 
984
      }
 
985
   }
 
986
   fclose(fh);
 
987
 
 
988
   return NULL;
 
989
}
 
990
#endif // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
991
 
 
992
 
 
993
/*
 
994
 *-----------------------------------------------------------------------------
 
995
 *
 
996
 * GHIPlatformUriPathToString --
 
997
 *
 
998
 *      Turns a UriPathSegment sequence into a '/' separated filesystem path.
 
999
 *
 
1000
 * Results:
 
1001
 *      Newly heap-allocated string containing the FS path.
 
1002
 *
 
1003
 * Side effects:
 
1004
 *      Allocates memory (caller is responsible for freeing it).
 
1005
 *
 
1006
 *-----------------------------------------------------------------------------
 
1007
 */
 
1008
 
 
1009
static char *
 
1010
GHIPlatformUriPathToString(UriPathSegmentA *path) // IN
 
1011
{
 
1012
   GString *str;
 
1013
   char *retval;
 
1014
   UriPathSegmentA *cur;
 
1015
 
 
1016
   str = g_string_new("");
 
1017
   for (cur = path; cur; cur = cur->next) {
 
1018
      g_string_append_c(str, '/');
 
1019
      g_string_append_len(str, cur->text.first, cur->text.afterLast - cur->text.first);
 
1020
   }
 
1021
 
 
1022
   retval = str->str;
 
1023
   g_string_free(str, FALSE);
 
1024
 
 
1025
   return retval;
 
1026
}
 
1027
 
 
1028
 
 
1029
/*
 
1030
 *-----------------------------------------------------------------------------
 
1031
 *
 
1032
 * GHIPlatformURIToArgs --
 
1033
 *
 
1034
 *      Turns a URI into an array of arguments that are useable for execing...
 
1035
 *
 
1036
 * Results:
 
1037
 *      TRUE if successful, FALSE otherwise.
 
1038
 *
 
1039
 * Side effects:
 
1040
 *      Allocates an array of strings, and returns it in *argv...
 
1041
 *
 
1042
 *-----------------------------------------------------------------------------
 
1043
 */
 
1044
 
 
1045
static Bool
 
1046
GHIPlatformURIToArgs(GHIPlatform *ghip,     // IN
 
1047
                     const char *uriString, // IN
 
1048
                     char ***argv,          // IN/OUT
 
1049
                     int *argc,             // IN/OUT
 
1050
                     char **dotDesktopPath) // IN/OUT
 
1051
{
 
1052
   UriParserStateA state;
 
1053
   UriUriA uri;
 
1054
   Bool parseQueryString = TRUE;
 
1055
   GPtrArray *newargv;
 
1056
 
 
1057
   ASSERT(ghip);
 
1058
   ASSERT(uriString);
 
1059
   ASSERT(argv);
 
1060
   ASSERT(argc);
 
1061
   ASSERT(dotDesktopPath);
 
1062
 
 
1063
   memset(&state, 0, sizeof state);
 
1064
   memset(&uri, 0, sizeof uri);
 
1065
   state.uri = &uri;
 
1066
   if (uriParseUriA(&state, uriString) != URI_SUCCESS) {
 
1067
      uriFreeUriMembersA(&uri);
 
1068
      return FALSE;
 
1069
   }
 
1070
 
 
1071
   newargv = g_ptr_array_new();
 
1072
 
 
1073
#if 0 // Temporary until ShellAction is implemented.
 
1074
   /*
 
1075
    * This is previous code that was used for mapping x-vmware-share and
 
1076
    * x-vmware-action URIs, but it's not being used at the moment.
 
1077
    */
 
1078
   if (URI_TEXTRANGE_EQUAL(uri.scheme, "x-vmware-share")) {
 
1079
      UriTextRangeA *sharename;
 
1080
      UriPathSegmentA *sharepath;
 
1081
      char *sharedir;
 
1082
      char *subdir;
 
1083
 
 
1084
      /*
 
1085
       * Try to find a mounted HGFS filesystem that has the right path...
 
1086
       * Deals with both share://sharename/baz/baz and share:///sharename/baz/baz
 
1087
       */
 
1088
      if (uri.hostText.first) {
 
1089
         sharename = &uri.hostText;
 
1090
         sharepath = uri.pathHead;
 
1091
      } else if (uri.pathHead) {
 
1092
         sharename = &uri.pathHead->text;
 
1093
         sharepath = uri.pathHead->next;
 
1094
      } else {
 
1095
         NOT_REACHED();
 
1096
      }
 
1097
 
 
1098
      sharedir = GHIPlatformFindHGFSShare(ghip, sharename);
 
1099
      if (!sharedir) {
 
1100
         uriFreeUriMembersA(&uri);
 
1101
         g_ptr_array_free(newargv, TRUE);
 
1102
         Debug("Couldn't find a mounted HGFS filesystem for %s\n", uriString);
 
1103
         return FALSE;
 
1104
      }
 
1105
 
 
1106
      subdir = GHIPlatformUriPathToString(sharepath);
 
1107
      g_ptr_array_add(newargv, g_strconcat(sharedir, subdir, NULL));
 
1108
      g_free(sharedir);
 
1109
      g_free(subdir);
 
1110
   } else if (URI_TEXTRANGE_EQUAL(uri.scheme, "x-vmware-action")) {
 
1111
      if (g_file_test("/usr/bin/gnome-open", G_FILE_TEST_IS_EXECUTABLE)) {
 
1112
         g_ptr_array_add(newargv, g_strdup("/usr/bin/gnome-open"));
 
1113
      } else if (g_file_test("/usr/bin/htmlview", G_FILE_TEST_IS_EXECUTABLE)
 
1114
                 && URI_TEXTRANGE_EQUAL(uri.hostText, "browse")) {
 
1115
         g_ptr_array_add(newargv, g_strdup("/usr/bin/htmlview"));
 
1116
      } else {
 
1117
         Debug("Don't know how to handle URI %s. "
 
1118
               "We definitely don't have /usr/bin/gnome-open.\n",
 
1119
               uriString);
 
1120
         NOT_IMPLEMENTED();
 
1121
      }
 
1122
   }
 
1123
#endif // Temporary until ShellAction is implemented.
 
1124
 
 
1125
   if (URI_TEXTRANGE_EQUAL(uri.scheme, "file")) {
 
1126
      char *fspath = GHIPlatformUriPathToString(uri.pathHead);
 
1127
      g_ptr_array_add(newargv, fspath);
 
1128
   } else {
 
1129
      /*
 
1130
       * Just append the unparsed URI as-is onto the command line.
 
1131
       */
 
1132
      g_ptr_array_add(newargv, g_strdup(uriString));
 
1133
      parseQueryString = FALSE;
 
1134
   }
 
1135
 
 
1136
   *dotDesktopPath = NULL;
 
1137
   if (parseQueryString) {
 
1138
      /*
 
1139
       * We may need additional command-line arguments from the part of the URI after the
 
1140
       * '?'.
 
1141
       */
 
1142
 
 
1143
      UriQueryListA *queryList;
 
1144
      int itemCount;
 
1145
 
 
1146
      if (uriDissectQueryMallocA(&queryList, &itemCount,
 
1147
                                 uri.query.first, uri.query.afterLast) == URI_SUCCESS) {
 
1148
         UriQueryListA *cur;
 
1149
 
 
1150
         for (cur = queryList; cur; cur = cur->next) {
 
1151
            if (!cur->value) {
 
1152
               continue;
 
1153
            }
 
1154
 
 
1155
            if (strcmp(cur->key, "argv[]") == 0) {
 
1156
               g_ptr_array_add(newargv, g_strdup(cur->value));
 
1157
               cur->value = NULL;
 
1158
            } else if (strcmp(cur->key, "DesktopEntry")) {
 
1159
               *dotDesktopPath = g_strdup(cur->value);
 
1160
            }
 
1161
         }
 
1162
 
 
1163
         uriFreeQueryListA(queryList);
 
1164
      } else {
 
1165
         Warning("Dissection of query string in URI %s failed\n",
 
1166
                 uriString);
 
1167
      }
 
1168
   }
 
1169
 
 
1170
   uriFreeUriMembersA(&uri);
 
1171
 
 
1172
   *argc = newargv->len;
 
1173
   g_ptr_array_add(newargv, NULL);
 
1174
   *argv = (char **) g_ptr_array_free(newargv, FALSE);
 
1175
 
 
1176
   return TRUE;
 
1177
}
 
1178
 
 
1179
 
 
1180
#if 0 // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
1181
/*
 
1182
 *-----------------------------------------------------------------------------
 
1183
 *
 
1184
 * GHIPlatformStripFieldCodes --
 
1185
 *
 
1186
 *      Strip field codes from an argv-style string array.
 
1187
 *
 
1188
 * Results:
 
1189
 *      None.
 
1190
 *
 
1191
 * Side effects:
 
1192
 *      Modifies the string array, possibly freeing some members.
 
1193
 *
 
1194
 *-----------------------------------------------------------------------------
 
1195
 */
 
1196
 
 
1197
static void
 
1198
GHIPlatformStripFieldCodes(char **argv, // IN/OUT
 
1199
                           int *argc)   // IN/OUT
 
1200
{
 
1201
   int i;
 
1202
 
 
1203
   ASSERT(argv);
 
1204
   ASSERT(argc);
 
1205
 
 
1206
   for (i = 0; i < *argc; i++) {
 
1207
      if (argv[i][0] == '%'
 
1208
          && argv[i][1] != '\0'
 
1209
          && argv[i][2] == '\0') {
 
1210
         g_free(argv[i]);
 
1211
         /*
 
1212
          * This math may look slightly dodgy - just remember that these
 
1213
          * argv's have a terminating NULL pointer, which is not included in its argc.
 
1214
          */
 
1215
         g_memmove(argv + i, argv + i + 1,
 
1216
                   (*argc - i) * sizeof *argv);
 
1217
         (*argc)--;
 
1218
      }
 
1219
   }
 
1220
}
 
1221
#endif // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
1222
 
 
1223
 
 
1224
/*
 
1225
 *-----------------------------------------------------------------------------
 
1226
 *
 
1227
 * GHIPlatformCombineArgs --
 
1228
 *
 
1229
 *      Takes a target URI and turns it into an argv array that we can actually
 
1230
 *      exec().
 
1231
 *
 
1232
 *      XXX TODO: accept location arguments once ShellAction is implemented.
 
1233
 *
 
1234
 * Results:
 
1235
 *      TRUE if successful, FALSE otherwise. If TRUE, fullArgv/fullArgc will
 
1236
 *      contain the exec-able argument array.
 
1237
 *
 
1238
 * Side effects:
 
1239
 *      Allocates a string array in fullArgv (owner is responsible for freeing).
 
1240
 *
 
1241
 *-----------------------------------------------------------------------------
 
1242
 */
 
1243
 
 
1244
static Bool
 
1245
GHIPlatformCombineArgs(GHIPlatform *ghip,            // IN
 
1246
                       const char *targetUtf8,       // IN
 
1247
                       char ***fullArgv,             // OUT
 
1248
                       int *fullArgc)                // OUT
 
1249
{
 
1250
   char **targetArgv = NULL;
 
1251
   int targetArgc = 0;
 
1252
   char *targetDotDesktop = NULL;
 
1253
   GPtrArray *fullargs = g_ptr_array_new();
 
1254
   GHIMenuItem *ghm = NULL;
 
1255
   int i;
 
1256
 
 
1257
   ASSERT(ghip);
 
1258
   ASSERT(targetUtf8);
 
1259
   ASSERT(fullArgv);
 
1260
   ASSERT(fullArgc);
 
1261
 
 
1262
   if (!GHIPlatformURIToArgs(ghip,
 
1263
                             targetUtf8,
 
1264
                             &targetArgv,
 
1265
                             &targetArgc,
 
1266
                             &targetDotDesktop)) {
 
1267
      Debug("Parsing URI %s failed\n", targetUtf8);
 
1268
      return FALSE;
 
1269
   }
 
1270
 
 
1271
#if 0 // Temporary until ShellAction is implemented.
 
1272
   /*
 
1273
    * This is previous code that was used for combining file and action
 
1274
    * arguments, but it's not being used at the moment. Our action URI format
 
1275
    * has changed, so this will need to be updated before it's usable.
 
1276
    */
 
1277
 
 
1278
   /*
 
1279
    * In the context of the .desktop spec
 
1280
    * (http://standards.freedesktop.org/desktop-entry-spec/1.1/ar01s06.html),
 
1281
    * combining the two is not as simple as just concatenating them.
 
1282
    *
 
1283
    * XXX for some random older programs, we may want to do concatenation in the future.
 
1284
    */
 
1285
   char **srcArgv;
 
1286
   int srcArgc;
 
1287
   char *srcDotDesktop = NULL;
 
1288
 
 
1289
   /*
 
1290
    * First, figure out which argv[] array is the 'main' one, and which one will serve
 
1291
    * only to fill in the file/URL argument in the .desktop file...
 
1292
    */
 
1293
   if (! *actionArgc) {
 
1294
      srcArgv = *fileArgv;
 
1295
      srcArgc = *fileArgc;
 
1296
      srcDotDesktop = fileDotDesktop;
 
1297
   } else {
 
1298
      srcArgv = *actionArgv;
 
1299
      srcArgc = *actionArgc;
 
1300
      srcDotDesktop = actionDotDesktop;
 
1301
      if (fileDotDesktop) {
 
1302
         GHIPlatformStripFieldCodes(*fileArgv, fileArgc);
 
1303
      }
 
1304
   }
 
1305
#endif // Temporary until ShellAction is implemented.
 
1306
 
 
1307
   for (i = 0; i < targetArgc; i++) {
 
1308
      const char *thisarg = targetArgv[i];
 
1309
 
 
1310
      if (thisarg[0] == '%' && thisarg[1] != '\0' && thisarg[2] == '\0') {
 
1311
         switch (thisarg[1]) {
 
1312
         case 'F': // %F expands to multiple filenames
 
1313
         case 'f': // %f expands to a filename
 
1314
            /*
 
1315
             * XXX TODO: add file location arguments
 
1316
             */
 
1317
            //if (srcArgv != *fileArgv && *fileArgc) {
 
1318
            //   g_ptr_array_add(fullargs, g_strdup((*fileArgv)[0]));
 
1319
            //}
 
1320
            break;
 
1321
         case 'U': // %U expands to multiple URLs
 
1322
         case 'u': // %u expands to a URL
 
1323
            /*
 
1324
             * XXX TODO: add URL location arguments
 
1325
             */
 
1326
            //if (srcArgv != *fileArgv && fileUtf8) {
 
1327
            //   g_ptr_array_add(fullargs, g_strdup(fileUtf8));
 
1328
            //}
 
1329
            break;
 
1330
 
 
1331
            /*
 
1332
             * These three require getting at the .desktop info for the app.
 
1333
             */
 
1334
         case 'k':
 
1335
         case 'i':
 
1336
         case 'c':
 
1337
            if (!ghm && targetDotDesktop) {
 
1338
               ghm = (GHIMenuItem *) g_hash_table_lookup(ghip->appsByDesktopEntry,
 
1339
                                                         targetDotDesktop);
 
1340
            }
 
1341
            if (!ghm) {
 
1342
               ASSERT (fullargs->len > 0);
 
1343
               ghm = (GHIMenuItem *) g_hash_table_lookup(ghip->appsByExecutable,
 
1344
                                                         g_ptr_array_index(fullargs, 0));
 
1345
            }
 
1346
 
 
1347
            if (ghm) {
 
1348
               switch (thisarg[1]) {
 
1349
               case 'c': // %c expands to the .desktop's Name=
 
1350
                  {
 
1351
                     char *ctmp =
 
1352
                        g_key_file_get_locale_string(ghm->keyfile,
 
1353
                                                     G_KEY_FILE_DESKTOP_GROUP,
 
1354
                                                     G_KEY_FILE_DESKTOP_KEY_NAME,
 
1355
                                                     NULL, NULL);
 
1356
                     if (ctmp) {
 
1357
                        g_ptr_array_add(fullargs, ctmp);
 
1358
                     }
 
1359
                  }
 
1360
                  break;
 
1361
               case 'i': // %i expands to "--icon" then the .desktop's Icon=
 
1362
                  {
 
1363
                     char *ctmp =
 
1364
                        g_key_file_get_string(ghm->keyfile,
 
1365
                                              G_KEY_FILE_DESKTOP_GROUP,
 
1366
                                              G_KEY_FILE_DESKTOP_KEY_ICON,
 
1367
                                              NULL);
 
1368
                     if (ctmp && *ctmp) {
 
1369
                        g_ptr_array_add(fullargs, g_strdup("--icon"));
 
1370
                        g_ptr_array_add(fullargs, ctmp);
 
1371
                     }
 
1372
                  }
 
1373
                  break;
 
1374
               case 'k': // %k expands to the .desktop's path
 
1375
                  g_ptr_array_add(fullargs, g_strdup(ghm->keyfilePath));
 
1376
                  break;
 
1377
               }
 
1378
            }
 
1379
            break;
 
1380
         case '%': // Expands to a literal
 
1381
            g_ptr_array_add(fullargs, g_strdup("%"));
 
1382
            break;
 
1383
         default:
 
1384
            /*
 
1385
             * Intentionally ignore an unknown field code.
 
1386
             */
 
1387
            break;
 
1388
         }
 
1389
      } else {
 
1390
         g_ptr_array_add(fullargs, g_strdup(thisarg));
 
1391
      }
 
1392
   }
 
1393
   *fullArgc = fullargs->len;
 
1394
   g_ptr_array_add(fullargs, NULL);
 
1395
   *fullArgv = (char **) g_ptr_array_free(fullargs, FALSE);
 
1396
 
 
1397
   g_strfreev(targetArgv);
 
1398
   g_free(targetDotDesktop);
 
1399
 
 
1400
   return *fullArgc ? TRUE : FALSE;
 
1401
}
 
1402
 
 
1403
 
 
1404
/*
 
1405
 *----------------------------------------------------------------------------
 
1406
 *
 
1407
 * GHIPlatformShellOpen --
 
1408
 *
 
1409
 *      Open the specified file with the default shell handler (ShellExecute).
 
1410
 *      Note that the file path may be either a URI (originated with
 
1411
 *      Tools >= NNNNN), or a regular path (originated with Tools < NNNNN).
 
1412
 *
 
1413
 * Results:
 
1414
 *      TRUE if successful, FALSE otherwise.
 
1415
 *
 
1416
 * Side effects:
 
1417
 *      None
 
1418
 *
 
1419
 *----------------------------------------------------------------------------
 
1420
 */
 
1421
 
 
1422
Bool
 
1423
GHIPlatformShellOpen(GHIPlatform *ghip,    // IN
 
1424
                     const char *fileUtf8) // IN
 
1425
{
 
1426
   char **fullArgv = NULL;
 
1427
   int fullArgc = 0;
 
1428
   Bool retval = FALSE;
 
1429
 
 
1430
   ASSERT(ghip);
 
1431
   ASSERT(fileUtf8);
 
1432
 
 
1433
   Debug("%s: file: '%s'\n", __FUNCTION__, fileUtf8);
 
1434
 
 
1435
   /*
 
1436
    * XXX This is not shippable.  GHIPlatformCombineArgs may still be necessary,
 
1437
    * and I chose to use if (1) rather than #if 0 it out in order to not have to
 
1438
    * #if 0 out that function and everything else it calls as well.
 
1439
    */
 
1440
   if (1) {
 
1441
      UriParserStateA upState;
 
1442
      UriUriA uri;
 
1443
 
 
1444
      memset(&upState, 0, sizeof upState);
 
1445
      memset(&uri, 0, sizeof uri);
 
1446
      upState.uri = &uri;
 
1447
 
 
1448
      if (uriParseUriA(&upState, fileUtf8) == URI_SUCCESS &&
 
1449
          URI_TEXTRANGE_EQUAL(uri.scheme, "file")) {
 
1450
         Bool success = FALSE;
 
1451
 
 
1452
         gchar* tmp = (gchar*)g_alloca(strlen(fileUtf8) + 1);
 
1453
         uriUriStringToUnixFilenameA(fileUtf8, tmp);
 
1454
 
 
1455
         Glib::ustring unixFile;
 
1456
         unixFile.assign(tmp);
 
1457
 
 
1458
         Glib::ustring contentType;
 
1459
         bool uncertain;
 
1460
         contentType = Gio::content_type_guess(unixFile, std::string(""), uncertain);
 
1461
 
 
1462
         if (contentType == "application/x-desktop") {
 
1463
            GDesktopAppInfo* dappinfo;
 
1464
            dappinfo = g_desktop_app_info_new_from_filename(unixFile.c_str());
 
1465
            if (dappinfo) {
 
1466
               GAppInfo *appinfo = (GAppInfo*)G_APP_INFO(dappinfo);
 
1467
               success = AppInfoLaunchEnv(ghip, appinfo);
 
1468
               g_object_unref(dappinfo);
 
1469
            }
 
1470
         } else if (Glib::file_test(unixFile, Glib::FILE_TEST_IS_REGULAR) &&
 
1471
                    Glib::file_test(unixFile, Glib::FILE_TEST_IS_EXECUTABLE)) {
 
1472
            std::vector<Glib::ustring> argv;
 
1473
            argv.push_back(unixFile);
 
1474
            try {
 
1475
               Glib::spawn_async("" /* inherit cwd */, argv, ghip->nativeEnviron, (Glib::SpawnFlags) 0);
 
1476
               success = TRUE;
 
1477
            } catch(Glib::SpawnError& e) {
 
1478
               g_warning("%s: %s: %s\n", __FUNCTION__, unixFile.c_str(), e.what().c_str());
 
1479
            }
 
1480
         } else {
 
1481
            std::vector<Glib::ustring> argv;
 
1482
            Glib::ustring de = Xdg_DetectDesktopEnv();
 
1483
            // XXX Really we should just use xdg-open exclusively, but xdg-open
 
1484
            // as shipped with xdg-utils 1.0.2 is broken.  It is fixed
 
1485
            // in portland CVS, but we need to import into modsource and
 
1486
            // redistribute with Tools in order to guarantee a working version.
 
1487
            if (de == "GNOME") {
 
1488
               argv.push_back("gnome-open");
 
1489
            } else if (de == "KDE") {
 
1490
               argv.push_back("kde-open");
 
1491
            } else {
 
1492
               argv.push_back("xdg-open");
 
1493
            }
 
1494
            argv.push_back(unixFile);
 
1495
            try {
 
1496
               Glib::spawn_async("", argv, ghip->nativeEnviron, Glib::SPAWN_SEARCH_PATH);
 
1497
               success = TRUE;
 
1498
            } catch(Glib::SpawnError& e) {
 
1499
               g_warning("%s: %s: %s\n", __FUNCTION__, unixFile.c_str(), e.what().c_str());
 
1500
            }
 
1501
         }
 
1502
 
 
1503
         return success;
 
1504
      }
 
1505
   } else if (GHIPlatformCombineArgs(ghip, fileUtf8, &fullArgv, &fullArgc) &&
 
1506
       fullArgc > 0) {
 
1507
      // XXX Will fix this soon.
 
1508
#if 0
 
1509
      retval = g_spawn_async(NULL, fullArgv,
 
1510
                            /*
 
1511
                             * XXX  Please don't hate me for casting off the qualifier
 
1512
                             * here.  Glib does -not- modify the environment, at
 
1513
                             * least not in the parent process, but their prototype
 
1514
                             * does not specify this argument as being const.
 
1515
                             *
 
1516
                             * Comment stolen from GuestAppX11OpenUrl.
 
1517
                             */
 
1518
                             (char **)ghip->nativeEnviron,
 
1519
                             (GSpawnFlags) (G_SPAWN_SEARCH_PATH |
 
1520
                             G_SPAWN_STDOUT_TO_DEV_NULL |
 
1521
                             G_SPAWN_STDERR_TO_DEV_NULL),
 
1522
                             NULL, NULL, NULL, NULL);
 
1523
#endif
 
1524
   }
 
1525
 
 
1526
   g_strfreev(fullArgv);
 
1527
 
 
1528
   return retval;
 
1529
}
 
1530
 
 
1531
 
 
1532
/*
 
1533
 *----------------------------------------------------------------------------
 
1534
 *
 
1535
 * GHIPlatformShellAction --
 
1536
 *      Perform the specified shell action with the optional target and
 
1537
 *      locations arguments. Note that the target may be either a URI
 
1538
 *      (originated with Tools >= NNNNN), or a regular path (originated with
 
1539
 *      Tools < NNNNN).
 
1540
 *      See the comment at ghIntegration.c::GHITcloShellAction for information
 
1541
 *      on the command format and supported actions.
 
1542
 *
 
1543
 * Results:
 
1544
 *      TRUE if successful, FALSE otherwise.
 
1545
 *
 
1546
 * Side effects:
 
1547
 *      None
 
1548
 *
 
1549
 *----------------------------------------------------------------------------
 
1550
 */
 
1551
 
 
1552
Bool
 
1553
GHIPlatformShellAction(GHIPlatform *ghip,          // IN: platform-specific state
 
1554
                       const char *actionURI,      // IN
 
1555
                       const char *targetURI,      // IN
 
1556
                       const char **locations,     // IN
 
1557
                       int numLocations)           // IN
 
1558
{
 
1559
   /*
 
1560
    * TODO: implement the shell action execution.
 
1561
    * The GHIPlatformShellUrlOpen() below is left for reference, but is not
 
1562
    * used right now. Its functionality should be integrated here.
 
1563
    */
 
1564
   ASSERT(ghip);
 
1565
   ASSERT(actionURI);
 
1566
   ASSERT(targetURI);
 
1567
 
 
1568
   Debug("%s not implemented yet.\n", __FUNCTION__);
 
1569
 
 
1570
   return FALSE;
 
1571
}
 
1572
 
 
1573
 
 
1574
#if 0 // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
1575
/*
 
1576
 *----------------------------------------------------------------------------
 
1577
 *
 
1578
 * GHIPlatformShellUrlOpen --
 
1579
 *
 
1580
 *      Run ShellExecute on a given file.
 
1581
 *
 
1582
 * Results:
 
1583
 *      TRUE if success, FALSE otherwise.
 
1584
 *
 
1585
 * Side effects:
 
1586
 *      None
 
1587
 *
 
1588
 *----------------------------------------------------------------------------
 
1589
 */
 
1590
 
 
1591
Bool
 
1592
GHIPlatformShellUrlOpen(GHIPlatform *ghip,      // IN: platform-specific state
 
1593
                        const char *fileUtf8,   // IN: command/file
 
1594
                        const char *actionUtf8) // IN: action
 
1595
{
 
1596
#ifdef GTK2
 
1597
   char **fileArgv = NULL;
 
1598
   int fileArgc = 0;
 
1599
   char *fileDotDesktop = NULL;
 
1600
   char **actionArgv = NULL;
 
1601
   int actionArgc = 0;
 
1602
   char *actionDotDesktop = NULL;
 
1603
   char **fullArgv = NULL;
 
1604
   int fullArgc = 0;
 
1605
 
 
1606
   Bool retval = FALSE;
 
1607
 
 
1608
   ASSERT(ghip);
 
1609
 
 
1610
   if (!GHIPlatformURIToArgs(ghip, fileUtf8, &fileArgv, &fileArgc,
 
1611
                             &fileDotDesktop)) {
 
1612
      Debug("Parsing URI %s failed\n", fileUtf8);
 
1613
      return FALSE;
 
1614
   }
 
1615
 
 
1616
   if (actionUtf8 && !GHIPlatformURIToArgs(ghip, actionUtf8, &actionArgv, &actionArgc,
 
1617
                                           &actionDotDesktop)) {
 
1618
      Debug("Parsing action URI %s failed\n", actionUtf8);
 
1619
      g_strfreev(fileArgv);
 
1620
      g_free(fileDotDesktop);
 
1621
      return FALSE;
 
1622
   }
 
1623
 
 
1624
   if (GHIPlatformCombineArgs(ghip,
 
1625
                              fileUtf8, &fileArgv, &fileArgc, fileDotDesktop,
 
1626
                              actionUtf8, &actionArgv, &actionArgc, actionDotDesktop,
 
1627
                              &fullArgv, &fullArgc)) {
 
1628
      retval = g_spawn_async(NULL, fullArgv, NULL,
 
1629
                             G_SPAWN_SEARCH_PATH |
 
1630
                             G_SPAWN_STDOUT_TO_DEV_NULL |
 
1631
                             G_SPAWN_STDERR_TO_DEV_NULL,
 
1632
                             NULL, NULL, NULL, NULL);
 
1633
   }
 
1634
 
 
1635
   g_strfreev(fileArgv);
 
1636
   g_free(fileDotDesktop);
 
1637
   g_strfreev(actionArgv);
 
1638
   g_free(actionDotDesktop);
 
1639
   g_strfreev(fullArgv);
 
1640
 
 
1641
   return retval;
 
1642
#else // !GTK2
 
1643
   return FALSE;
 
1644
#endif // GTK2
 
1645
}
 
1646
#endif // REMOVE AFTER IMPLEMENTING GHIPlatformShellAction
 
1647
 
 
1648
 
 
1649
/*
 
1650
 *----------------------------------------------------------------------------
 
1651
 *
 
1652
 * GHIPlatformSetGuestHandler --
 
1653
 *
 
1654
 *      Set the handler for the specified filetype (or URL protocol) to the
 
1655
 *      given value.
 
1656
 *
 
1657
 * Results:
 
1658
 *      TRUE if successful, FALSE otherwise.
 
1659
 *
 
1660
 * Side effects:
 
1661
 *      None
 
1662
 *
 
1663
 *----------------------------------------------------------------------------
 
1664
 */
 
1665
 
 
1666
Bool
 
1667
GHIPlatformSetGuestHandler(GHIPlatform *ghip,    // IN: platform-specific state
 
1668
                           const char *suffix,   // IN/OPT: suffix
 
1669
                           const char *mimeType, // IN/OPT: MIME Type
 
1670
                           const char *UTI,      // IN/OPT: UTI
 
1671
                           const char *actionURI,  // IN:
 
1672
                           const char *targetURI)  // IN:
 
1673
{
 
1674
   ASSERT(ghip);
 
1675
 
 
1676
   return FALSE;
 
1677
}
 
1678
 
 
1679
 
 
1680
/*
 
1681
 *----------------------------------------------------------------------------
 
1682
 *
 
1683
 * GHIPlatformRestoreDefaultGuestHandler --
 
1684
 *
 
1685
 *      Restore the handler for a given type to the value in use before any
 
1686
 *      changes by tools.
 
1687
 *
 
1688
 * Results:
 
1689
 *      TRUE if successful, FALSE otherwise.
 
1690
 *
 
1691
 * Side effects:
 
1692
 *      None
 
1693
 *
 
1694
 *----------------------------------------------------------------------------
 
1695
 */
 
1696
 
 
1697
Bool
 
1698
GHIPlatformRestoreDefaultGuestHandler(GHIPlatform *ghip,  // IN: platform-specific state
 
1699
                                      const char *suffix,   // IN/OPT: Suffix
 
1700
                                      const char *mimetype, // IN/OPT: MIME Type
 
1701
                                      const char *UTI)      // IN/OPT: UTI
 
1702
{
 
1703
   ASSERT(ghip);
 
1704
 
 
1705
   return FALSE;
 
1706
}
 
1707
 
 
1708
 
 
1709
/*
 
1710
 *----------------------------------------------------------------------------
 
1711
 *
 
1712
 * GHIPlatformSetOutlookTempFolder --
 
1713
 *
 
1714
 *    Set the temporary folder used by Microsoft Outlook to store attachments
 
1715
 *    opened by the user.
 
1716
 *
 
1717
 *    XXX While we probably won't ever need to implement this for Linux, we
 
1718
 *        still the definition of this function in the X11 back-end.
 
1719
 *
 
1720
 * Results:
 
1721
 *    TRUE if successful, FALSE otherwise.
 
1722
 *
 
1723
 * Side effects:
 
1724
 *    None
 
1725
 *
 
1726
 *----------------------------------------------------------------------------
 
1727
 */
 
1728
 
 
1729
Bool
 
1730
GHIPlatformSetOutlookTempFolder(GHIPlatform *ghip,       // IN: platform-specific state
 
1731
                                const char *targetURI)   // IN: Target URI
 
1732
{
 
1733
   ASSERT(ghip);
 
1734
   ASSERT(targetURI);
 
1735
 
 
1736
   return FALSE;
 
1737
}
 
1738
 
 
1739
 
 
1740
/* @brief Send a mouse or keyboard event to a tray icon.
 
1741
 *
 
1742
 * @param[in] ghip Pointer to platform-specific GHI data.
 
1743
 *
 
1744
 * @retval TRUE Operation Succeeded.
 
1745
 * @retval FALSE Operation Failed.
 
1746
 */
 
1747
 
 
1748
Bool
 
1749
GHIPlatformTrayIconSendEvent(GHIPlatform *ghip,
 
1750
                             const char *iconID,
 
1751
                             uint32 event,
 
1752
                             uint32 x,
 
1753
                             uint32 y)
 
1754
{
 
1755
   ASSERT(ghip);
 
1756
   ASSERT(iconID);
 
1757
   return FALSE;
 
1758
}
 
1759
 
 
1760
/* @brief Start sending tray icon updates to the VMX.
 
1761
 *
 
1762
 * @param[in] ghip Pointer to platform-specific GHI data.
 
1763
 *
 
1764
 * @retval TRUE Operation Succeeded.
 
1765
 * @retval FALSE Operation Failed.
 
1766
 */
 
1767
 
 
1768
Bool
 
1769
GHIPlatformTrayIconStartUpdates(GHIPlatform *ghip)
 
1770
{
 
1771
   ASSERT(ghip);
 
1772
   return FALSE;
 
1773
}
 
1774
 
 
1775
/* @brief Stop sending tray icon updates to the VMX.
 
1776
 *
 
1777
 * @param[in] ghip Pointer to platform-specific GHI data.
 
1778
 *
 
1779
 * @retval TRUE Operation Succeeded.
 
1780
 * @retval FALSE Operation Failed.
 
1781
 */
 
1782
 
 
1783
Bool
 
1784
GHIPlatformTrayIconStopUpdates(GHIPlatform *ghip)
 
1785
{
 
1786
   ASSERT(ghip);
 
1787
   return FALSE;
 
1788
}
 
1789
 
 
1790
/* @brief Set a window to be focused.
 
1791
 *
 
1792
 * @param[in] ghip Pointer to platform-specific GHI data.
 
1793
 * @param[in] xdrs Pointer to serialized data from the host.
 
1794
 *
 
1795
 * @retval TRUE Operation Succeeded.
 
1796
 * @retval FALSE Operation Failed.
 
1797
 */
 
1798
 
 
1799
Bool
 
1800
GHIPlatformSetFocusedWindow(GHIPlatform *ghip,
 
1801
                            int32 windowId)
 
1802
{
 
1803
   ASSERT(ghip);
 
1804
   return FALSE;
 
1805
}
 
1806
 
 
1807
 
 
1808
/**
 
1809
 * @brief Get the hash (or timestamp) of information returned by
 
1810
 * GHIPlatformGetBinaryInfo.
 
1811
 *
 
1812
 * @param[in]  ghip     Pointer to platform-specific GHI data.
 
1813
 * @param[in]  request  Request containing which executable to get the hash for.
 
1814
 * @param[out] reply    Reply to be filled with the hash.
 
1815
 *
 
1816
 * @retval TRUE Operation succeeded.
 
1817
 * @retval FALSE Operation failed.
 
1818
 */
 
1819
 
 
1820
Bool GHIPlatformGetExecInfoHash(GHIPlatform *ghip,
 
1821
                                const char *execPath,
 
1822
                                char **execInfoHash)
 
1823
{
 
1824
   ASSERT(ghip);
 
1825
   ASSERT(execPath);
 
1826
   ASSERT(execInfoHash);
 
1827
 
 
1828
   return FALSE;
 
1829
}
 
1830
 
 
1831
 
 
1832
/*
 
1833
 ******************************************************************************
 
1834
 * GHIX11FindDesktopUriByExec --                                         */ /**
 
1835
 *
 
1836
 * Given an executable path, attempt to generate an "execUri" associated with a
 
1837
 * corresponding .desktop file.
 
1838
 *
 
1839
 * @sa GHIX11_FindDesktopUriByExec
 
1840
 *
 
1841
 * @note Returned pointer belongs to the GHI module.  Caller must not free it.
 
1842
 *
 
1843
 * @param[in]  ghip     GHI platform-specific context.
 
1844
 * @param[in]  execPath Input binary path.  May be absolute or relative.
 
1845
 *
 
1846
 * @return Pointer to a URI string on success, NULL on failure.
 
1847
 *
 
1848
 ******************************************************************************
 
1849
 */
 
1850
 
 
1851
const gchar *
 
1852
GHIX11FindDesktopUriByExec(GHIPlatform *ghip,
 
1853
                           const char *exec)
 
1854
{
 
1855
   char pathbuf[MAXPATHLEN];
 
1856
   gchar *pathname = NULL;
 
1857
   gchar *uri = NULL;
 
1858
   GHIMenuItem *gmi;
 
1859
   gboolean fudged = FALSE;
 
1860
   gboolean basenamed = FALSE;
 
1861
 
 
1862
   ASSERT(ghip);
 
1863
   ASSERT(exec);
 
1864
 
 
1865
   /*
 
1866
    * XXX This is not shippable.  This is to be addressed by milestone 3 with
 
1867
    * the improved "fuzzy logic for UNITY_RPC_GET_WINDOW_PATH" deliverable.
 
1868
    */
 
1869
 
 
1870
   return NULL;
 
1871
 
 
1872
   /*
 
1873
    * Check our hash table first.  Negative entries are also cached.
 
1874
    */
 
1875
   if (g_hash_table_lookup_extended(ghip->appsByWindowExecutable,
 
1876
                                    exec, NULL, (gpointer*)&uri)) {
 
1877
      return uri;
 
1878
   }
 
1879
 
 
1880
   /*
 
1881
    * Okay, execPath may be absolute or relative.
 
1882
    *
 
1883
    * We'll search for a matching .desktop entry using the following methods:
 
1884
    *
 
1885
    * 1.  Use absolute path of exec.
 
1886
    * 2.  Use absolute path of basename of exec.  (Resolves /opt/Adobe/Reader9/
 
1887
    *     Reader/intellinux/bin/acroread to /usr/bin/acroread.)
 
1888
    * 3.  Consult whitelist of known applications and guess at possible
 
1889
    *     launchers.  (firefox-bin => firefox, soffice.bin => ooffice.)
 
1890
    */
 
1891
 
 
1892
   /*
 
1893
    * Attempt #1:  Start with unmodified input.
 
1894
    */
 
1895
   Str_Strcpy(pathbuf, exec, sizeof pathbuf);
 
1896
 
 
1897
tryagain:
 
1898
   g_free(pathname);    // Placed here rather than at each goto.  I'm lazy.
 
1899
 
 
1900
   pathname = g_find_program_in_path(pathbuf);
 
1901
   if (pathname) {
 
1902
      gmi = (GHIMenuItem*)g_hash_table_lookup(ghip->appsByExecutable, pathname);
 
1903
      if (gmi) {
 
1904
         uri = GHIPlatformMenuItemToURI(ghip, gmi);
 
1905
      }
 
1906
   }
 
1907
 
 
1908
   if (!uri) {
 
1909
      /*
 
1910
       * Attempt #2:  Take the basename of exec.
 
1911
       */
 
1912
      if (!basenamed) {
 
1913
         char tmpbuf[MAXPATHLEN];
 
1914
         char *ctmp;
 
1915
 
 
1916
         basenamed = TRUE;
 
1917
 
 
1918
         /* basename(3) may modify the input buffer, so make a temporary copy. */
 
1919
         Str_Strcpy(tmpbuf, pathbuf, sizeof tmpbuf);
 
1920
         ctmp = basename(tmpbuf);
 
1921
         if (ctmp != NULL) {
 
1922
            Str_Strcpy(pathbuf, ctmp, sizeof pathbuf);
 
1923
            goto tryagain;
 
1924
         }
 
1925
      }
 
1926
 
 
1927
      /*
 
1928
       * Attempt #3:  Get our whitelist on.
 
1929
       */
 
1930
      if (!fudged) {
 
1931
         static struct {
 
1932
            const gchar *pattern;
 
1933
            const gchar *exec;
 
1934
         } fudgePatterns[] = {
 
1935
            /*
 
1936
             * XXX Worth compiling once?  Consider placing in an external filter
 
1937
             * file to allow users to update it themselves easily.
 
1938
             */
 
1939
            { "*firefox*-bin", "firefox" },
 
1940
            { "*thunderbird*-bin", "thunderbird" },
 
1941
            { "*soffice.bin", "ooffice" }
 
1942
         };
 
1943
         unsigned int i;
 
1944
 
 
1945
         fudged = TRUE;
 
1946
 
 
1947
         for (i = 0; i < ARRAYSIZE(fudgePatterns); i++) {
 
1948
            if (g_pattern_match_simple(fudgePatterns[i].pattern,
 
1949
                                       pathbuf)) {
 
1950
               Str_Strcpy(pathbuf, fudgePatterns[i].exec, sizeof pathbuf);
 
1951
               goto tryagain;
 
1952
            }
 
1953
         }
 
1954
      }
 
1955
   }
 
1956
 
 
1957
   g_free(pathname);
 
1958
 
 
1959
   /*
 
1960
    * Cache the result, even if it was negative.
 
1961
    */
 
1962
   g_hash_table_insert(ghip->appsByWindowExecutable, g_strdup(exec), uri);
 
1963
 
 
1964
   return uri;
 
1965
}
 
1966
 
 
1967
 
 
1968
/*
 
1969
 *-----------------------------------------------------------------------------
 
1970
 *
 
1971
 * AppInfoLaunchEnv --
 
1972
 *
 
1973
 *      Wrapper around g_app_info_launch which takes a custom environment into
 
1974
 *      account.
 
1975
 *
 
1976
 *      GHI/X11 should spawn applications using ghip->nativeEnviron, but
 
1977
 *      g_app_info_launch doesn't taken a custom environment as a parameter.
 
1978
 *      Rather than reimplement that function, we work around it by doing the
 
1979
 *      following:
 
1980
 *
 
1981
 *         Parent:
 
1982
 *         1.  Fork a child process.
 
1983
 *         2.  Block until child terminates, returning true if the child exited
 
1984
 *             with an exit code of 0.
 
1985
 *
 
1986
 *         Child:
 
1987
 *         1.  Flush the environment and build a new one from nativeEnviron.
 
1988
 *         2.  Spawn desired application with g_app_info_launch.
 
1989
 *         3.  Exit with 0 if spawn was successful, otherwise 1.
 
1990
 *
 
1991
 * Results:
 
1992
 *      Returns true if launch succeeded, false otherwise.
 
1993
 *
 
1994
 * Side effects:
 
1995
 *      Creates a child process and blocks until the child exits.  Child lasts
 
1996
 *      only as long as it takes to call g_app_info_launch.
 
1997
 *
 
1998
 *-----------------------------------------------------------------------------
 
1999
 */
 
2000
 
 
2001
static bool
 
2002
AppInfoLaunchEnv(GHIPlatform* ghip,     // IN
 
2003
                 GAppInfo* appInfo)     // IN
 
2004
{
 
2005
   bool success = false;
 
2006
 
 
2007
   pid_t myPid = fork();
 
2008
   switch (myPid) {
 
2009
   case -1:
 
2010
      /* Error. */
 
2011
      g_warning("%s: fork: %s\n", __FUNCTION__, strerror(errno));
 
2012
      break;
 
2013
 
 
2014
   case 0:
 
2015
      /* Child:  Exit with _exit() so as to not trigger any atexit() routines. */
 
2016
      {
 
2017
         if (clearenv() == 0) {
 
2018
            std::vector<Glib::ustring>::iterator envp;
 
2019
            for (envp = ghip->nativeEnviron.begin();
 
2020
                 envp != ghip->nativeEnviron.end();
 
2021
                 ++envp) {
 
2022
               /*
 
2023
                * The string passed to putenv() becomes part of the environment --
 
2024
                * it isn't copied.  That's fine, though, because we're running in
 
2025
                * the context of a very short-lived wrapper process.
 
2026
                */
 
2027
               if (putenv((char*)envp->c_str()) != 0) {
 
2028
                  g_warning("%s: failed to restore native environment\n", __FUNCTION__);
 
2029
                  _exit(1);
 
2030
               }
 
2031
            }
 
2032
            success = g_app_info_launch(appInfo, NULL, NULL, NULL);
 
2033
         }
 
2034
         _exit(success == true ? 0 : 1);
 
2035
      }
 
2036
      break;
 
2037
 
 
2038
   default:
 
2039
      /* Parent:  Hang out until our child terminates. */
 
2040
      {
 
2041
         int status = 0;
 
2042
         int ret = -1;
 
2043
         while (1) {
 
2044
            ret = waitpid(myPid, &status, 0);
 
2045
            if ((ret == -1 && errno != EINTR) ||
 
2046
                (ret == myPid && (WIFEXITED(status) || WIFSIGNALED(status)))) {
 
2047
               break;
 
2048
            }
 
2049
         }
 
2050
         success = (ret == myPid) && WIFEXITED(status) && WEXITSTATUS(status) == 0;
 
2051
      }
 
2052
   }
 
2053
 
 
2054
   return success;
 
2055
}
 
2056
 
 
2057
 
 
2058
/*
 
2059
 *-----------------------------------------------------------------------------
 
2060
 *
 
2061
 * OnMenusChanged --
 
2062
 *
 
2063
 *      Signal handler for updates to launch or fixed menus.
 
2064
 *
 
2065
 * Results:
 
2066
 *      None.
 
2067
 *
 
2068
 * Side effects:
 
2069
 *      Calls transport's launchMenuChange callback, if set.
 
2070
 *
 
2071
 *-----------------------------------------------------------------------------
 
2072
 */
 
2073
 
 
2074
static void
 
2075
OnMenusChanged(GHIPlatform *ghip)       // IN
 
2076
{
 
2077
   if (ghip->hostCallbacks.launchMenuChange) {
 
2078
      std::vector<const char *> folderKeysChanged;
 
2079
      folderKeysChanged.push_back(UNITY_START_MENU_LAUNCH_FOLDER);
 
2080
      folderKeysChanged.push_back(UNITY_START_MENU_FIXED_FOLDER);
 
2081
      ghip->hostCallbacks.launchMenuChange(folderKeysChanged.size(),
 
2082
                                           &folderKeysChanged[0]);
 
2083
   }
 
2084
}