~n-muench/ubuntu/quantal/open-vm-tools/open-vm-tools.may2.sid-sync

« back to all changes in this revision

Viewing changes to vmware-user/foreignVMToolsDaemon.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Baumann
  • Date: 2009-05-30 09:48:43 UTC
  • mfrom: (1.1.5 upstream) (2.4.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090530094843-gdpza57r5iqsf124
Tags: 2009.05.22-167859-1
MergingĀ upstreamĀ versionĀ 2009.05.22-167859.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*********************************************************
2
 
 * Copyright (C) 2007 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
 
 * foreignVMToolsDaemon.c --
21
 
 *
22
 
 * This implements the Vix tools using a socket to connect to the tools
23
 
 * from a client. This also assumes there is no VMX process, so the
24
 
 * tools takes commands directly from the client over the socket.
25
 
 *
26
 
 * In a VMware VM:
27
 
 *   Client ---socket--->  VMX  ---backdoor---> Tools
28
 
 *
29
 
 * In a foreign VM:
30
 
 *   Client ---socket--->  Tools
31
 
 *
32
 
 * So, this code performs operations on behalf of the VMX and the tools.
33
 
 * The tools functions are all implemented by the vixTools library, which
34
 
 * is the same code that runs in the VMware tools. This module then handles
35
 
 * socket communication and does functions that would otherwise be done by
36
 
 * the VMX.
37
 
 *
38
 
 */
39
 
 
40
 
#include <stdio.h>
41
 
#include <stdlib.h>
42
 
#include <errno.h>
43
 
#include <stdarg.h>
44
 
 
45
 
#ifdef _WIN32
46
 
#include <winsock2.h>
47
 
#include <ws2tcpip.h>
48
 
#include <MSWSock.h>
49
 
#else
50
 
#include <sys/types.h>
51
 
#include <sys/poll.h>
52
 
#include <sys/socket.h>
53
 
#include <netdb.h>
54
 
#include <netinet/in.h>
55
 
#include <netinet/tcp.h>
56
 
#include <arpa/inet.h>
57
 
#include <fcntl.h>
58
 
#if defined(__FreeBSD__) || defined(sun) || defined(__APPLE__)
59
 
#include <unistd.h>
60
 
#else
61
 
#include <linux/unistd.h>
62
 
#endif
63
 
#endif
64
 
 
65
 
#include "vmware.h"
66
 
#include "vm_version.h"
67
 
#include "vm_app.h"
68
 
#include "message.h"
69
 
#include "eventManager.h"
70
 
#include "debug.h"
71
 
#include "util.h"
72
 
#include "strutil.h"
73
 
#include "str.h"
74
 
#include "err.h"
75
 
#include "hostinfo.h"
76
 
#include "guest_os.h"
77
 
#include "conf.h"
78
 
#include "base64.h"
79
 
#include "hgfsServer.h"
80
 
#include "hgfs.h"
81
 
#include "system.h"
82
 
#include "codeset.h"
83
 
 
84
 
#include "vixOpenSource.h"
85
 
#include "syncEvent.h"
86
 
#include "foundryThreads.h"
87
 
#include "vixCommands.h"
88
 
#include "foreignVMToolsDaemon.h"
89
 
 
90
 
#include "vixTools.h"
91
 
 
92
 
 
93
 
VixLockType                         globalLock;
94
 
static struct FoundryWorkerThread   *selectThread;
95
 
static DblLnkLst_Links              *gEventQueue;
96
 
 
97
 
ForeignVMToolsConnection            *activeConnectionList = NULL;
98
 
ForeignVMToolsCommand               *globalCommandList = NULL;
99
 
 
100
 
static struct GuestApp_Dict         *configDictionary = NULL;
101
 
 
102
 
static Bool ForeignToolsIsCommandAlive(ForeignVMToolsCommand *asyncCommand);
103
 
 
104
 
static ForeignVMToolsCommand *ForeignToolsGetActiveCommand(const char *name);
105
 
 
106
 
static void ForeignToolsSendRunProgramResponse(const char *requestName,
107
 
                                               VixError resultErr,
108
 
                                               int exitCode,
109
 
                                               int64 pid,
110
 
                                               void *clientData);
111
 
 
112
 
static VixError ForeignToolsGetUserCredentialForGuest(ForeignVMToolsConnection *connectionState,
113
 
                                                      ForeignVMToolsCommand *commandState);
114
 
 
115
 
static VixError ForeignToolsGetProperties(ForeignVMToolsCommand *asyncCommand,
116
 
                                          VixMsgTrivialRequest *requestMsg);
117
 
 
118
 
static VixError ForeignToolsSetProperties(ForeignVMToolsCommand *asyncCommand,
119
 
                                          VixMsgSetVMStateRequest *requestMsg);
120
 
 
121
 
static VixError ForeignToolsGetToolsState(ForeignVMToolsCommand *asyncCommand,
122
 
                                          VixMsgTrivialRequest *requestMsg);
123
 
 
124
 
 
125
 
/*
126
 
 *-----------------------------------------------------------------------------
127
 
 *
128
 
 * ForeignTools_Initialize --
129
 
 *
130
 
 *      Start a worker thread.
131
 
 *
132
 
 * Results:
133
 
 *      FoundryWorkerThread *
134
 
 *
135
 
 * Side effects:
136
 
 *
137
 
 *-----------------------------------------------------------------------------
138
 
 */
139
 
 
140
 
Bool
141
 
ForeignTools_Initialize(GuestApp_Dict *configDictionaryParam,     // IN
142
 
                        DblLnkLst_Links *eventQueue)              // IN
143
 
{
144
 
   VixError err = VIX_OK;
145
 
   Bool success = TRUE;
146
 
 
147
 
   MessageStub_RegisterTransport();
148
 
   gEventQueue = eventQueue;
149
 
 
150
 
   /*
151
 
    * Initialize the limited global state that protects us when 
152
 
    * client applications explicitly pump events.
153
 
    */
154
 
   err = VIX_INIT_LOCK(&globalLock);
155
 
   if (VIX_OK != err) {
156
 
      success = FALSE;
157
 
      goto abort;
158
 
   }
159
 
 
160
 
   if (NULL == configDictionaryParam) {
161
 
      success = FALSE;
162
 
      goto abort;
163
 
   }
164
 
   configDictionary = configDictionaryParam;
165
 
 
166
 
   VixTools_SetConsoleUserPolicy(TRUE); // allowConsoleUserOpsParam
167
 
   VixTools_SetRunProgramCallback(ForeignToolsSendRunProgramResponse, NULL);
168
 
 
169
 
   success = ForeignTools_InitializeNetworking();
170
 
   if (!success) {
171
 
      goto abort;
172
 
   }
173
 
 
174
 
   /*
175
 
    * Start the worker threads that will pump poll.
176
 
    */
177
 
   selectThread = FoundryThreads_StartThread(ForeignToolsSelectLoop, NULL);
178
 
   if (NULL == selectThread) {
179
 
      goto abort;
180
 
   }
181
 
 
182
 
   return(TRUE);
183
 
 
184
 
abort:
185
 
   return(FALSE);
186
 
} // ForeignTools_Initialize
187
 
 
188
 
 
189
 
/*
190
 
 *-----------------------------------------------------------------------------
191
 
 *
192
 
 * ForeignTools_Shutdown --
193
 
 *
194
 
 *      Shutdown a thread and destroys its thread state.
195
 
 * 
196
 
 * Results:
197
 
 *      None.
198
 
 *
199
 
 * Side effects:
200
 
 *
201
 
 *-----------------------------------------------------------------------------
202
 
 */
203
 
 
204
 
void
205
 
ForeignTools_Shutdown(void)
206
 
{
207
 
   /*
208
 
    * Tell the select thread to exit and wait for it to stop.
209
 
    */
210
 
   selectThread->stopThread = TRUE;
211
 
   ForeignToolsWakeSelectThread();
212
 
   if (NULL != selectThread) {
213
 
      FoundryThreads_StopThread(selectThread);
214
 
      selectThread = NULL;
215
 
   }
216
 
 
217
 
   /*
218
 
    * Close every connection.
219
 
    */
220
 
   VIX_ENTER_LOCK(&globalLock);
221
 
   while (NULL != activeConnectionList) {
222
 
      ForeignToolsCloseConnection(activeConnectionList, SHUTDOWN_FOR_SYSTEM_SHUTDOWN);
223
 
   }
224
 
   VIX_LEAVE_LOCK(&globalLock);
225
 
 
226
 
   /*
227
 
    * Shut down the work queue.
228
 
    */
229
 
   VIX_DELETE_LOCK(&globalLock);
230
 
} // ForeignTools_Shutdown
231
 
 
232
 
 
233
 
/*
234
 
 *----------------------------------------------------------------------------
235
 
 *
236
 
 * ForeignToolsIsCommandAlive --
237
 
 *
238
 
 *      Returns TRUE if ForeignVMToolsCommand is still in the list of
239
 
 *      active commands.  Otherwise, return FALSE.
240
 
 *
241
 
 * Results:
242
 
 *      None.
243
 
 *
244
 
 * Side effects:
245
 
 *
246
 
 *----------------------------------------------------------------------------
247
 
 */
248
 
 
249
 
Bool
250
 
ForeignToolsIsCommandAlive(ForeignVMToolsCommand *asyncCommand) // IN
251
 
{
252
 
   ForeignVMToolsCommand *command;
253
 
   ForeignVMToolsCommand *nextCommand;
254
 
   Bool isAlive = FALSE;
255
 
 
256
 
   if (NULL == asyncCommand) {
257
 
      goto abort;
258
 
   }
259
 
 
260
 
   VIX_ENTER_LOCK(&globalLock);
261
 
 
262
 
   command = globalCommandList;
263
 
   while (NULL != command) {
264
 
      nextCommand = command->next;
265
 
      if (command == asyncCommand) {
266
 
         isAlive = TRUE;
267
 
         break;
268
 
      }
269
 
      command = nextCommand;
270
 
   }
271
 
 
272
 
   VIX_LEAVE_LOCK(&globalLock);
273
 
 
274
 
abort:
275
 
   return(isAlive);
276
 
} // ForeignToolsIsCommandAlive
277
 
 
278
 
 
279
 
/*
280
 
 *-----------------------------------------------------------------------------
281
 
 *
282
 
 * ForeignToolsDiscardCommand --
283
 
 *
284
 
 *      Record that we are executing an async command.
285
 
 *
286
 
 * Results:
287
 
 *      None.
288
 
 *
289
 
 * Side effects:
290
 
 *      None.
291
 
 *
292
 
 *-----------------------------------------------------------------------------
293
 
 */
294
 
 
295
 
void
296
 
ForeignToolsDiscardCommand(ForeignVMToolsCommand *command)   // IN
297
 
{
298
 
   ForeignVMToolsCommand *targetCommand;
299
 
   ForeignVMToolsCommand *prevCommand;
300
 
 
301
 
   if ((NULL == command) || (NULL == command->connection)) {
302
 
      return;
303
 
   }
304
 
 
305
 
   VIX_ENTER_LOCK(&globalLock);
306
 
 
307
 
   targetCommand = globalCommandList;
308
 
   prevCommand = NULL;
309
 
   while (NULL != targetCommand) {
310
 
      if (targetCommand == command) {
311
 
         break;
312
 
      }
313
 
      prevCommand = targetCommand;
314
 
      targetCommand = targetCommand->next;
315
 
   }
316
 
 
317
 
   /*
318
 
    * If the command is not in the active list, then somebody else
319
 
    * already freed it. Do not delete it again.
320
 
    */
321
 
   if (NULL == targetCommand) {
322
 
      goto abort;
323
 
   }
324
 
 
325
 
   if (NULL != prevCommand) {
326
 
      prevCommand->next = targetCommand->next;
327
 
   } else {
328
 
      globalCommandList = targetCommand->next;
329
 
   }
330
 
 
331
 
   free(command->responseBody);
332
 
   free(command->guestUserNamePassword);
333
 
   free(command->obfuscatedGuestUserNamePassword);
334
 
   free(command);
335
 
 
336
 
abort:
337
 
   VIX_LEAVE_LOCK(&globalLock);
338
 
} // ForeignToolsDiscardCommand
339
 
 
340
 
 
341
 
/*
342
 
 *-----------------------------------------------------------------------------
343
 
 *
344
 
 * ForeignToolsGetActiveCommand --
345
 
 *
346
 
 *       This gets the named active state.
347
 
 *
348
 
 * Results:
349
 
 *       The named active state.
350
 
 *
351
 
 * Side effects:
352
 
 *    None.
353
 
 *
354
 
 *-----------------------------------------------------------------------------
355
 
 */
356
 
 
357
 
ForeignVMToolsCommand *
358
 
ForeignToolsGetActiveCommand(const char *name)  // IN
359
 
{
360
 
   ForeignVMToolsCommand *command = NULL;
361
 
 
362
 
   /*
363
 
    * Look for the test that corresponds to this name.
364
 
    */
365
 
   VIX_ENTER_LOCK(&globalLock);
366
 
 
367
 
   command = globalCommandList;
368
 
   while (NULL != command) {
369
 
      if (0 == Str_Strcasecmp(name, command->asyncOpName)) {
370
 
         break;
371
 
      }
372
 
 
373
 
      command = command->next;
374
 
   }
375
 
 
376
 
   VIX_LEAVE_LOCK(&globalLock);
377
 
 
378
 
   return command;
379
 
} // ForeignToolsGetActiveCommand
380
 
 
381
 
 
382
 
 
383
 
/*
384
 
 *----------------------------------------------------------------------------
385
 
 *
386
 
 * ForeignToolsSendRunProgramResponse --
387
 
 *
388
 
 * Results:
389
 
 *      None.
390
 
 *
391
 
 * Side effects:
392
 
 *      None.
393
 
 *
394
 
 *----------------------------------------------------------------------------
395
 
 */
396
 
 
397
 
void
398
 
ForeignToolsSendRunProgramResponse(const char *requestName, // IN
399
 
                                   VixError resultErr,      // IN
400
 
                                   int exitCode,            // IN
401
 
                                   int64 pid,               // IN
402
 
                                   void *clientData)        // IN
403
 
{
404
 
   VixError err = VIX_OK;
405
 
   int additionalError = 0;
406
 
   ForeignVMToolsCommand *asyncCommand = NULL;
407
 
   VmTimeType programStopTime;
408
 
   VmTimeType deltaTime = 0;
409
 
   VixMsgRunProgramResponse responseMessage;
410
 
 
411
 
   Hostinfo_GetTimeOfDay(&programStopTime);
412
 
 
413
 
   asyncCommand = ForeignToolsGetActiveCommand(requestName);
414
 
   if (NULL == asyncCommand) {
415
 
      return;
416
 
   }
417
 
   /*
418
 
    * If all we wanted to do was start the program, then we are
419
 
    * done.
420
 
    */
421
 
   if (asyncCommand->runProgramOptions & VIX_RUNPROGRAM_RETURN_IMMEDIATELY) {
422
 
      return;
423
 
   }
424
 
 
425
 
   err = resultErr;
426
 
 
427
 
   /*
428
 
    * Find how long the program was running. Convert to seconds,
429
 
    * and report the result in VMDB.
430
 
    */
431
 
   deltaTime = programStopTime - asyncCommand->programStartTime;
432
 
   deltaTime = deltaTime / 1000000;
433
 
 
434
 
   responseMessage.exitCode = exitCode;
435
 
   responseMessage.deltaTime = deltaTime;
436
 
   responseMessage.pid = pid;
437
 
   responseMessage.stdOutLength = 0;
438
 
   responseMessage.stdErrLength = 0;
439
 
 
440
 
   ForeignToolsSendResponseUsingTotalMessage(asyncCommand->connection,
441
 
                                             &(asyncCommand->requestHeader),
442
 
                                             sizeof responseMessage,
443
 
                                             &responseMessage,
444
 
                                             err,
445
 
                                             additionalError,
446
 
                                             VIX_RESPONSE_EXTENDED_RESULT_V1);
447
 
   ForeignToolsDiscardCommand(asyncCommand);
448
 
} // ForeignToolsSendRunProgramResponse
449
 
 
450
 
 
451
 
/*
452
 
 *-----------------------------------------------------------------------------
453
 
 *
454
 
 * ForeignToolsGetUserCredentialForGuest -- 
455
 
 *
456
 
 *      Get the credentials we will pass into the guest.
457
 
 *      These may be passed in with the original command request, or else we 
458
 
 *      may use some default values. 
459
 
 *
460
 
 *      This also does limited checking, mainly to see if any credentials are
461
 
 *      even provided. It does NOT check to see if a user/name password is valid,
462
 
 *      or if a particular user is authorized for some operation. That will
463
 
 *      be done later in the guest when we actually execute each operation.
464
 
 *
465
 
 *      This leaves the actual credentials for this command packaged in 
466
 
 *      commandState->obfuscatedGuestUserNamePassword 
467
 
 *
468
 
 * Results:
469
 
 *      VixError
470
 
 *
471
 
 * Side effects:
472
 
 *      None.
473
 
 *
474
 
 *-----------------------------------------------------------------------------
475
 
 */
476
 
 
477
 
VixError
478
 
ForeignToolsGetUserCredentialForGuest(ForeignVMToolsConnection *connectionState,   // IN
479
 
                                      ForeignVMToolsCommand *commandState)         // IN
480
 
{
481
 
   VixError err = VIX_OK;
482
 
   //CryptoError cryptoErr = CRYPTO_ERROR_SUCCESS;
483
 
   Bool success;
484
 
   char *guestUserName = NULL;
485
 
   char *guestPassword = NULL;
486
 
   char *namePasswordMessage = NULL;
487
 
   char *endCredential;
488
 
   char *encryptedNamePassword = NULL;
489
 
   size_t encryptedNamePasswordLength = 0;
490
 
   char *decryptedBuffer = NULL;
491
 
   size_t decryptedBufferLength = 0;
492
 
   VixCommandNamePassword *guestUserNamePassword;
493
 
   VixCommandNamePassword newUserNamePassword;
494
 
   size_t newCredentialsLength;
495
 
   size_t newMessageLength;
496
 
   char *newNamePasswordBuffer;
497
 
   //CryptoKeyedHash *keyedHash;
498
 
 
499
 
   /*
500
 
    * If there was an optional userName and password sent, then parse it now.
501
 
    */
502
 
   if (VIX_USER_CREDENTIAL_NAME_PASSWORD == commandState->requestHeader.userCredentialType) {
503
 
      if (commandState->requestHeader.commonHeader.credentialLength <= 0) {
504
 
         err = VIX_E_INVALID_MESSAGE_HEADER;
505
 
         goto abort;
506
 
      }
507
 
 
508
 
      namePasswordMessage = connectionState->completeRequest 
509
 
                                 + commandState->requestHeader.commonHeader.headerLength 
510
 
                                 + commandState->requestHeader.commonHeader.bodyLength;
511
 
      endCredential = namePasswordMessage
512
 
                                 + commandState->requestHeader.commonHeader.credentialLength;
513
 
      /*
514
 
       * Make sure this is a valid NULL-terminated C string.
515
 
       */
516
 
      if (*(endCredential - 1)) {
517
 
         err = VIX_E_INVALID_MESSAGE_HEADER;
518
 
         goto abort;
519
 
      }
520
 
 
521
 
      success = Base64_EasyDecode(namePasswordMessage,
522
 
                                  (uint8 **) &encryptedNamePassword, 
523
 
                                  &encryptedNamePasswordLength);
524
 
      if (!success) {
525
 
         err = VIX_E_INVALID_MESSAGE_HEADER;
526
 
         goto abort;
527
 
      }
528
 
 
529
 
#if 0
530
 
      cryptoErr = CryptoKeyedHash_FromString(CryptoKeyedHashName_HMAC_SHA_1,
531
 
                                             &keyedHash);
532
 
      if (CryptoError_IsFailure(cryptoErr)) {
533
 
         err = Vix_TranslateCryptoError(cryptoErr);
534
 
         goto abort;
535
 
      }
536
 
 
537
 
      cryptoErr = CryptoKey_DecryptWithMAC(connectionState->sessionKey, 
538
 
                                           keyedHash,
539
 
                                           (uint8 *) encryptedNamePassword,
540
 
                                           encryptedNamePasswordLength,
541
 
                                           (uint8 **) &decryptedBuffer, 
542
 
                                           &decryptedBufferLength);
543
 
      if (CryptoError_IsFailure(cryptoErr)) {
544
 
         err = Vix_TranslateCryptoError(cryptoErr);
545
 
         goto abort;
546
 
      }
547
 
#else
548
 
      decryptedBuffer = encryptedNamePassword;
549
 
      decryptedBufferLength = encryptedNamePasswordLength;
550
 
#endif
551
 
 
552
 
      /*
553
 
       * Get the name/password fields from the credential data structure.
554
 
       */
555
 
      guestUserNamePassword = (VixCommandNamePassword *) decryptedBuffer;
556
 
      guestUserName = (char *) guestUserNamePassword;
557
 
      guestUserName += sizeof(VixCommandNamePassword);
558
 
      guestPassword = guestUserName;
559
 
      guestPassword += guestUserNamePassword->nameLength + 1;
560
 
 
561
 
      /*
562
 
       * If the client sent a valid userName/password, then this is OK.
563
 
       * Send it on to the tools and they will check permissions.
564
 
       * Allow an empty password string, that may be valid for some accounts.
565
 
       */
566
 
      if ((NULL == guestUserName) || !(guestUserName[0])) {
567
 
         err = VIX_E_GUEST_USER_PERMISSIONS;
568
 
         goto abort;
569
 
      }
570
 
 
571
 
      commandState->obfuscatedCredentialType = commandState->requestHeader.userCredentialType;
572
 
   /////////////////////////////////////////////////////////////////////////////
573
 
   } else if ((VIX_USER_CREDENTIAL_ANONYMOUS == commandState->requestHeader.userCredentialType)
574
 
         || (VIX_USER_CREDENTIAL_NONE == commandState->requestHeader.userCredentialType)) {
575
 
      err = VIX_E_MISSING_ANON_GUEST_ACCOUNT;
576
 
      goto abort;
577
 
   /////////////////////////////////////////////////////////////////////////////
578
 
   } else if (VIX_USER_CREDENTIAL_ROOT == commandState->requestHeader.userCredentialType) {
579
 
      err = VIX_E_ROOT_GUEST_OPERATIONS_PROHIBITED;
580
 
      goto abort;
581
 
   /////////////////////////////////////////////////////////////////////////////
582
 
   } else if (VIX_USER_CREDENTIAL_CONSOLE_USER == commandState->requestHeader.userCredentialType) {
583
 
      //<> For debug only. I need this until I package the tools as
584
 
      // an NT service. Otherwise, I cannot run a program.
585
 
      //<><>err = VIX_E_CONSOLE_GUEST_OPERATIONS_PROHIBITED;
586
 
      //<><>goto abort;
587
 
   } else {
588
 
      err = VIX_E_UNRECOGNIZED_COMMAND;
589
 
      goto abort;
590
 
   }
591
 
 
592
 
   /*
593
 
    * Now, package the name/password to be sent to the guest.
594
 
    */
595
 
   commandState->obfuscatedGuestUserNamePassword 
596
 
      = VixMsg_ObfuscateNamePassword(guestUserName, guestPassword);
597
 
 
598
 
   newCredentialsLength = sizeof(VixCommandNamePassword)
599
 
                           + strlen(commandState->obfuscatedGuestUserNamePassword) + 1;
600
 
 
601
 
   newMessageLength = commandState->requestHeader.commonHeader.totalMessageLength
602
 
                        - commandState->requestHeader.commonHeader.credentialLength
603
 
                        + newCredentialsLength;
604
 
 
605
 
   connectionState->completeRequest = Util_SafeRealloc(connectionState->completeRequest,
606
 
                                                       newMessageLength);
607
 
   if (NULL != guestUserName) {
608
 
      newUserNamePassword.nameLength = strlen(guestUserName);
609
 
   } else {
610
 
      newUserNamePassword.nameLength = 0;
611
 
   }
612
 
   if (NULL != guestPassword) {
613
 
      newUserNamePassword.passwordLength = strlen(guestPassword);
614
 
   } else {
615
 
      newUserNamePassword.passwordLength = 0;
616
 
   }
617
 
   newNamePasswordBuffer = connectionState->completeRequest 
618
 
                           + commandState->requestHeader.commonHeader.headerLength 
619
 
                           + commandState->requestHeader.commonHeader.bodyLength;
620
 
 
621
 
   memcpy(newNamePasswordBuffer,
622
 
          &newUserNamePassword,
623
 
          sizeof(newUserNamePassword));
624
 
   newNamePasswordBuffer += sizeof(newUserNamePassword);
625
 
   memcpy(newNamePasswordBuffer,
626
 
          commandState->obfuscatedGuestUserNamePassword,
627
 
          strlen(commandState->obfuscatedGuestUserNamePassword) + 1);
628
 
 
629
 
   commandState->requestHeader.commonHeader.totalMessageLength = newMessageLength;
630
 
   commandState->requestHeader.commonHeader.credentialLength = newCredentialsLength;
631
 
 
632
 
   connectionState->requestHeader.commonHeader.totalMessageLength = newMessageLength;
633
 
   connectionState->requestHeader.commonHeader.credentialLength = newCredentialsLength;
634
 
 
635
 
 
636
 
abort:
637
 
   free(encryptedNamePassword);
638
 
#if 0
639
 
   Crypto_Free(decryptedBuffer, decryptedBufferLength);
640
 
#endif
641
 
 
642
 
   return(err);
643
 
} // ForeignToolsGetUserCredentialForGuest
644
 
 
645
 
 
646
 
/*
647
 
 *-----------------------------------------------------------------------------
648
 
 *
649
 
 * ForeignToolsGetProperties --
650
 
 *
651
 
 * Results:
652
 
 *      VixError
653
 
 *
654
 
 * Side effects:
655
 
 *      None.
656
 
 *
657
 
 *-----------------------------------------------------------------------------
658
 
 */
659
 
 
660
 
VixError
661
 
ForeignToolsGetProperties(ForeignVMToolsCommand *asyncCommand,   // IN
662
 
                          VixMsgTrivialRequest *requestMsg)    // IN
663
 
{
664
 
   VixError err = VIX_OK;
665
 
   VixPropertyListImpl propList;
666
 
   char *serializedBufferBody = NULL;
667
 
   size_t serializedBufferLength = 0;
668
 
   char *responseMessage = NULL;
669
 
   size_t responseMessageLength = 0;
670
 
   int guestOSID;
671
 
   int guestOSFamily = GUEST_OS_FAMILY_ANY;   
672
 
 
673
 
   guestOSID = 0; // GuestOS_GetOSID(NULL);
674
 
 
675
 
 
676
 
   VixPropertyList_Initialize(&propList);
677
 
 
678
 
   err = VixPropertyList_SetInteger(&propList,
679
 
                                    VIX_PROPERTY_VM_TOOLS_STATE,
680
 
                                    VIX_TOOLSSTATE_RUNNING);
681
 
   if (VIX_OK != err) {
682
 
      goto abort;
683
 
   }
684
 
   err = VixPropertyList_SetString(&propList,
685
 
                                   VIX_PROPERTY_VMX_VERSION,
686
 
                                   "Foreign VM Tools");
687
 
   if (VIX_OK != err) {
688
 
      goto abort;
689
 
   }
690
 
   err = VixPropertyList_SetString(&propList,
691
 
                                   VIX_PROPERTY_FOREIGN_VM_TOOLS_VERSION,
692
 
                                   VIX_FOREIGN_VM_TOOLS_VMX_VERSION_STRING);
693
 
   if (VIX_OK != err) {
694
 
      goto abort;
695
 
   }
696
 
   err = VixPropertyList_SetString(&propList,
697
 
                                   VIX_PROPERTY_VMX_PRODUCT_NAME,
698
 
                                   PRODUCT_NAME);
699
 
   if (VIX_OK != err) {
700
 
      goto abort;
701
 
   }
702
 
   err = VixPropertyList_SetInteger(&propList,
703
 
                                    VIX_PROPERTY_VMX_VIX_FEATURES,
704
 
                                    VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE);
705
 
   if (VIX_OK != err) {
706
 
      goto abort;
707
 
   }
708
 
 
709
 
   /*
710
 
    * *****************************************
711
 
    * Now, fill in default values for the tools.
712
 
    * Later, if the tools are running, they will have a chance to
713
 
    * provide correct values.
714
 
    */
715
 
   err = VixPropertyList_SetString(&propList,
716
 
                                   VIX_PROPERTY_GUEST_TOOLS_PRODUCT_NAM,
717
 
                                   "");
718
 
   if (VIX_OK != err) {
719
 
      goto abort;
720
 
   }
721
 
   err = VixPropertyList_SetString(&propList,
722
 
                                   VIX_PROPERTY_GUEST_TOOLS_VERSION,
723
 
                                   "");
724
 
   if (VIX_OK != err) {
725
 
      goto abort;
726
 
   }
727
 
   err = VixPropertyList_SetInteger(&propList,
728
 
                                    VIX_PROPERTY_GUEST_TOOLS_API_OPTIONS,
729
 
                                    0);
730
 
   if (VIX_OK != err) {
731
 
      goto abort;
732
 
   }
733
 
   err = VixPropertyList_SetInteger(&propList,
734
 
                                    VIX_PROPERTY_GUEST_OS_FAMILY,
735
 
                                    guestOSFamily);
736
 
 
737
 
   if (VIX_OK != err) {
738
 
      goto abort;
739
 
   }
740
 
   err = VixPropertyList_SetString(&propList,
741
 
                                   VIX_PROPERTY_GUEST_NAME,
742
 
                                   "");
743
 
   if (VIX_OK != err) {
744
 
      goto abort;
745
 
   }
746
 
   err = VixPropertyList_SetString(&propList,
747
 
                                   VIX_PROPERTY_GUEST_POWER_OFF_SCRIPT,
748
 
                                   "");
749
 
   if (VIX_OK != err) {
750
 
      goto abort;
751
 
   }
752
 
   err = VixPropertyList_SetString(&propList,
753
 
                                   VIX_PROPERTY_GUEST_POWER_ON_SCRIPT,
754
 
                                   "");
755
 
   if (VIX_OK != err) {
756
 
      goto abort;
757
 
   }
758
 
   err = VixPropertyList_SetString(&propList,
759
 
                                   VIX_PROPERTY_GUEST_SUSPEND_SCRIPT,
760
 
                                   "");
761
 
   if (VIX_OK != err) {
762
 
      goto abort;
763
 
   }
764
 
   err = VixPropertyList_SetString(&propList,
765
 
                                   VIX_PROPERTY_GUEST_RESUME_SCRIPT,
766
 
                                   "");
767
 
   if (VIX_OK != err) {
768
 
      goto abort;
769
 
   }
770
 
 
771
 
   /*
772
 
    * Serialize the property list to buffer
773
 
    */
774
 
   err = VixPropertyList_Serialize(&propList,
775
 
                                   FALSE,
776
 
                                   &serializedBufferLength,
777
 
                                   &serializedBufferBody);
778
 
   if (VIX_OK != err) {
779
 
      goto abort;
780
 
   }
781
 
 
782
 
   responseMessageLength = sizeof(VixMsgGetVMStateResponse) 
783
 
                                + serializedBufferLength;
784
 
   responseMessage = Util_SafeMalloc(responseMessageLength);
785
 
   memcpy(responseMessage + sizeof(VixMsgGetVMStateResponse), 
786
 
          serializedBufferBody,
787
 
          serializedBufferLength);
788
 
   ((VixMsgGetVMStateResponse *) responseMessage)->bufferSize = serializedBufferLength;
789
 
 
790
 
   ForeignToolsSendResponseUsingTotalMessage(asyncCommand->connection,
791
 
                                           &(asyncCommand->requestHeader),
792
 
                                           responseMessageLength,
793
 
                                           responseMessage,
794
 
                                           err,
795
 
                                           0, // additionalError,
796
 
                                           0); // responseFlags
797
 
   ForeignToolsDiscardCommand(asyncCommand);
798
 
 
799
 
   /*
800
 
    * ForeignToolsSendResponseUsingTotalMessage owns responseMessage now,
801
 
    * and it will deallocate it.
802
 
    */
803
 
   responseMessage = NULL;
804
 
 
805
 
abort:
806
 
   free(responseMessage);
807
 
   free(serializedBufferBody);
808
 
   VixPropertyList_RemoveAllWithoutHandles(&propList);
809
 
 
810
 
   return err;
811
 
} // ForeignToolsGetProperties
812
 
 
813
 
 
814
 
/*
815
 
 *-----------------------------------------------------------------------------
816
 
 *
817
 
 * ForeignToolsSetProperties --
818
 
 *
819
 
 * Results:
820
 
 *      VixError
821
 
 *
822
 
 * Side effects:
823
 
 *      None.
824
 
 *
825
 
 *-----------------------------------------------------------------------------
826
 
 */
827
 
 
828
 
VixError
829
 
ForeignToolsSetProperties(ForeignVMToolsCommand *asyncCommand,      // IN
830
 
                          VixMsgSetVMStateRequest *requestMsg)    // IN
831
 
{
832
 
   VixError err = VIX_OK;
833
 
   VixPropertyListImpl propList;
834
 
   size_t serializedBufferLength = 0;
835
 
   char *serializedBuffer = NULL;
836
 
 
837
 
   /*
838
 
    * Do some validation
839
 
    */
840
 
   if ((NULL == asyncCommand)
841
 
         || (NULL == requestMsg)
842
 
         || (0 > requestMsg->bufferSize)) {
843
 
      err = VIX_E_INVALID_ARG;
844
 
      goto abort;
845
 
   }
846
 
 
847
 
   serializedBufferLength = requestMsg->bufferSize;
848
 
   serializedBuffer = ((char*)requestMsg + sizeof(*requestMsg));
849
 
 
850
 
   /*
851
 
    * Create a temporary property list and deserialize the buffer into.
852
 
    */
853
 
   err = VixPropertyList_Deserialize(&propList, 
854
 
                                     serializedBuffer, 
855
 
                                     serializedBufferLength);
856
 
   if (VIX_OK != err) {
857
 
      goto abort;
858
 
   }
859
 
 
860
 
   /*
861
 
    * To Do: <>
862
 
    * Change any VMX properties here.
863
 
    */
864
 
 
865
 
abort:
866
 
   VixPropertyList_RemoveAllWithoutHandles(&propList);
867
 
 
868
 
   return err;
869
 
} // ForeignToolsSetProperties
870
 
 
871
 
 
872
 
/*
873
 
 *-----------------------------------------------------------------------------
874
 
 *
875
 
 * ForeignToolsGetToolsState --
876
 
 *
877
 
 * Results:
878
 
 *      VixError
879
 
 *
880
 
 * Side effects:
881
 
 *      None.
882
 
 *
883
 
 *-----------------------------------------------------------------------------
884
 
 */
885
 
 
886
 
VixError
887
 
ForeignToolsGetToolsState(ForeignVMToolsCommand *asyncCommand,   // IN
888
 
                          VixMsgTrivialRequest *requestMsg)     // IN
889
 
{
890
 
   VixError err = VIX_OK;
891
 
   VixPropertyListImpl propList;
892
 
   char *decodedStr = NULL;
893
 
   size_t decodedLength;
894
 
   Bool success;
895
 
   char *serializedBufferBody = NULL;
896
 
   size_t serializedBufferLength = 0;
897
 
   Bool deleteResultValue = FALSE;
898
 
   char *base64Buffer = NULL;
899
 
   size_t base64BufferLength;
900
 
   char *responseMessage = NULL;
901
 
   size_t responseMessageLength = 0;
902
 
 
903
 
   VixPropertyList_Initialize(&propList);
904
 
 
905
 
   err = VixTools_ProcessVixCommand((VixCommandRequestHeader *) asyncCommand,
906
 
                                    asyncCommand->asyncOpName,
907
 
                                    1024 * 1024, // maxResultBufferSize,
908
 
                                    &configDictionary,
909
 
                                    gEventQueue,
910
 
                                    &base64Buffer,
911
 
                                    &base64BufferLength,
912
 
                                    &deleteResultValue);
913
 
   if (VIX_OK != err) {
914
 
      goto abort;
915
 
   }
916
 
 
917
 
   /*
918
 
    * If we got a string back from the guest, then decode it and 
919
 
    * convert it into a list of properties.
920
 
    */
921
 
   if (NULL != base64Buffer) {
922
 
      decodedStr = Util_SafeMalloc(base64BufferLength);
923
 
      success = Base64_Decode(base64Buffer, 
924
 
                              decodedStr, 
925
 
                              base64BufferLength,
926
 
                              &decodedLength);
927
 
      if (success) {
928
 
         err = VixPropertyList_Deserialize(&propList, decodedStr, decodedLength);
929
 
         err = VIX_OK;
930
 
      }
931
 
 
932
 
      (void) VixPropertyList_SetInteger(&propList,
933
 
                                        VIX_PROPERTY_VM_TOOLS_STATE,
934
 
                                        VIX_TOOLSSTATE_RUNNING);
935
 
   } else { // if (NULL == base64Buffer)
936
 
      (void) VixPropertyList_SetInteger(&propList,
937
 
                                        VIX_PROPERTY_VM_TOOLS_STATE,
938
 
                                        VIX_TOOLSSTATE_NOT_INSTALLED);
939
 
   }
940
 
 
941
 
   /*
942
 
    * Serialize the property list to buffer
943
 
    */
944
 
   err = VixPropertyList_Serialize(&propList,
945
 
                                   FALSE,
946
 
                                   &serializedBufferLength,
947
 
                                   &serializedBufferBody);
948
 
   if (VIX_OK != err) {
949
 
      goto abort;
950
 
   }
951
 
 
952
 
   responseMessageLength = sizeof(VixMsgGetVMStateResponse) 
953
 
                                + serializedBufferLength;
954
 
   responseMessage = Util_SafeMalloc(responseMessageLength);
955
 
   memcpy(responseMessage + sizeof(VixMsgGetVMStateResponse), 
956
 
          serializedBufferBody,
957
 
          serializedBufferLength);
958
 
   ((VixMsgGetVMStateResponse *) responseMessage)->bufferSize = serializedBufferLength;
959
 
 
960
 
   ForeignToolsSendResponseUsingTotalMessage(asyncCommand->connection,
961
 
                                             &(asyncCommand->requestHeader),
962
 
                                             responseMessageLength,
963
 
                                             responseMessage,
964
 
                                             err,
965
 
                                             0, // additionalError,
966
 
                                             0);  // responseFlags
967
 
   ForeignToolsDiscardCommand(asyncCommand);
968
 
 
969
 
   /*
970
 
    * VMAutomation_SendResponseUsingTotalMessage owns responseMessage now,
971
 
    * and it will deallocate it.
972
 
    */
973
 
   responseMessage = NULL;
974
 
 
975
 
abort:
976
 
   VixPropertyList_RemoveAllWithoutHandles(&propList);
977
 
   free(decodedStr);
978
 
   free(serializedBufferBody);
979
 
   free(responseMessage);
980
 
 
981
 
   return err;
982
 
} // ForeignToolsGetToolsState
983
 
 
984
 
 
985
 
/*
986
 
 *----------------------------------------------------------------------------
987
 
 *
988
 
 * ForeignToolsProcessMessage --
989
 
 *
990
 
 *      Calls the correct handler for a particular message type,
991
 
 *      and determines whether to queue more receives.
992
 
 *
993
 
 * Results:
994
 
 *      None.
995
 
 *
996
 
 * Side effects:
997
 
 *      None.
998
 
 *
999
 
 *----------------------------------------------------------------------------
1000
 
 */
1001
 
 
1002
 
void
1003
 
ForeignToolsProcessMessage(ForeignVMToolsConnection *connectionState)     //IN
1004
 
{
1005
 
   VixError err = VIX_OK;
1006
 
   ForeignVMToolsCommand *commandState = NULL;
1007
 
   uint32 additionalError = 0;
1008
 
   Bool sendResponse = FALSE;
1009
 
   Bool deleteResultValue = FALSE;
1010
 
   char *dummyResponse;
1011
 
   size_t dummyResponseLength;
1012
 
 
1013
 
   ASSERT(NULL != connectionState);
1014
 
 
1015
 
   /*
1016
 
    * Allocate state for the command. 
1017
 
    */
1018
 
   commandState = Util_SafeCalloc(1, sizeof *commandState);
1019
 
   commandState->connection = connectionState;
1020
 
   commandState->requestHeader = connectionState->requestHeader;
1021
 
   commandState->guestCredentialType = connectionState->requestHeader.userCredentialType;
1022
 
   commandState->guestUserNamePassword = NULL;
1023
 
   commandState->obfuscatedGuestUserNamePassword = NULL;
1024
 
   commandState->obfuscatedCredentialType = 0;
1025
 
   commandState->runProgramOptions = 0;
1026
 
   commandState->responseBody = NULL;
1027
 
   commandState->responseBodyLength = 0;
1028
 
 
1029
 
   VIX_ENTER_LOCK(&globalLock);
1030
 
   commandState->next = globalCommandList;
1031
 
   globalCommandList = commandState;
1032
 
   VIX_LEAVE_LOCK(&globalLock);
1033
 
 
1034
 
   switch (connectionState->requestHeader.opCode) {
1035
 
      ///////////////////////////////////
1036
 
      case VIX_COMMAND_GET_HANDLE_STATE:
1037
 
         err = ForeignToolsGetProperties(commandState, 
1038
 
                                       (VixMsgTrivialRequest *) connectionState->completeRequest);
1039
 
         break;
1040
 
 
1041
 
      ///////////////////////////////////
1042
 
      case VIX_COMMAND_INSTALL_TOOLS:
1043
 
      case VIX_COMMAND_WAIT_FOR_TOOLS:
1044
 
         err = VIX_OK;
1045
 
         sendResponse = TRUE;
1046
 
         break;
1047
 
 
1048
 
      ///////////////////////////////////
1049
 
      case VIX_COMMAND_GET_DISK_PROPERTIES:
1050
 
      case VIX_COMMAND_CAPTURE_SCREEN:
1051
 
      case VIX_COMMAND_MOUSE_EVENTS:
1052
 
      case VIX_COMMAND_KEYSTROKES:
1053
 
      case VIX_COMMAND_LIST_USB_DEVICES:
1054
 
         err = VIX_E_NOT_SUPPORTED;
1055
 
         break;
1056
 
 
1057
 
      ///////////////////////////////////
1058
 
      case VIX_CREATE_SESSION_KEY_COMMAND:
1059
 
         err = VIX_E_NOT_SUPPORTED;
1060
 
         break;
1061
 
 
1062
 
      ///////////////////////////////////
1063
 
      case VIX_COMMAND_SET_HANDLE_STATE:
1064
 
         err = ForeignToolsSetProperties(commandState, 
1065
 
                                         (VixMsgSetVMStateRequest *) (connectionState->completeRequest));
1066
 
         sendResponse = TRUE;
1067
 
         break;
1068
 
 
1069
 
      ///////////////////////////////////
1070
 
      case VIX_COMMAND_RUN_PROGRAM:
1071
 
         err = ForeignToolsGetUserCredentialForGuest(connectionState, commandState);
1072
 
         if (VIX_OK != err) {
1073
 
            goto abort;
1074
 
         }
1075
 
 
1076
 
         Hostinfo_GetTimeOfDay(&(commandState->programStartTime));
1077
 
         Str_Snprintf(commandState->asyncOpName, 
1078
 
                      sizeof(commandState->asyncOpName),
1079
 
                      "%p", 
1080
 
                      commandState);
1081
 
         commandState->responseBody = NULL;
1082
 
 
1083
 
         err = VixTools_ProcessVixCommand((VixCommandRequestHeader *) connectionState->completeRequest,
1084
 
                                          commandState->asyncOpName,
1085
 
                                          1024 * 1024, // maxResultBufferSize,
1086
 
                                          &configDictionary,
1087
 
                                          gEventQueue,
1088
 
                                          &dummyResponse,
1089
 
                                          &dummyResponseLength,
1090
 
                                          &deleteResultValue);
1091
 
 
1092
 
         /*
1093
 
          * We don't complete the command until the program exits.
1094
 
          */
1095
 
         commandState->responseBody = NULL;
1096
 
         commandState->responseBodyLength = 0;
1097
 
         break;
1098
 
 
1099
 
#if 0
1100
 
      ///////////////////////////////////
1101
 
      //<><><>
1102
 
      // I can add these.
1103
 
      case VIX_COMMAND_VM_POWEROFF:
1104
 
      case VIX_COMMAND_VM_RESET:
1105
 
      case VIX_COMMAND_RELOAD_VM:
1106
 
         break;
1107
 
 
1108
 
      ///////////////////////////////////
1109
 
      //<><><>
1110
 
      // These will not be supported.
1111
 
      case VIX_COMMAND_VM_SUSPEND:
1112
 
      case VIX_COMMAND_SET_NIC_BANDWIDTH:
1113
 
      case VIX_COMMAND_UPGRADE_VIRTUAL_HARDWARE:
1114
 
      case VIX_COMMAND_CREATE_RUNNING_VM_SNAPSHOT:
1115
 
      case VIX_COMMAND_CONSOLIDATE_RUNNING_VM_SNAPSHOT:
1116
 
         break;
1117
 
#endif
1118
 
 
1119
 
      ///////////////////////////////////
1120
 
      case VIX_COMMAND_GET_TOOLS_STATE:
1121
 
         err = ForeignToolsGetToolsState(commandState,
1122
 
                                         (VixMsgTrivialRequest *) (connectionState->completeRequest));
1123
 
         break;
1124
 
 
1125
 
      ////////////////////////////////////
1126
 
      case VIX_COMMAND_CHECK_USER_ACCOUNT:
1127
 
      case VIX_COMMAND_LOGOUT_IN_GUEST:
1128
 
         err = ForeignToolsGetUserCredentialForGuest(connectionState, commandState);
1129
 
         if (VIX_OK != err) {
1130
 
            goto abort;
1131
 
         }
1132
 
 
1133
 
         if (VIX_USER_CREDENTIAL_NAME_PASSWORD 
1134
 
                  == commandState->requestHeader.userCredentialType) {
1135
 
            err = VixTools_ProcessVixCommand((VixCommandRequestHeader *) connectionState->completeRequest,
1136
 
                                             commandState->asyncOpName,
1137
 
                                             1024 * 1024, // maxResultBufferSize,
1138
 
                                             &configDictionary,
1139
 
                                             gEventQueue,
1140
 
                                             &(commandState->responseBody),
1141
 
                                             &(commandState->responseBodyLength),
1142
 
                                             &deleteResultValue);
1143
 
         } else {
1144
 
            commandState->responseBody = NULL;
1145
 
            commandState->responseBodyLength = 0;
1146
 
         }
1147
 
 
1148
 
         sendResponse = TRUE;
1149
 
         break;
1150
 
 
1151
 
 
1152
 
      ///////////////////////////////////
1153
 
      // By default, most tools commands do require authentication.
1154
 
      default:
1155
 
         err = ForeignToolsGetUserCredentialForGuest(connectionState, commandState);
1156
 
         if (VIX_OK != err) {
1157
 
            goto abort;
1158
 
         }
1159
 
 
1160
 
         err = VixTools_ProcessVixCommand((VixCommandRequestHeader *) connectionState->completeRequest,
1161
 
                                          commandState->asyncOpName,
1162
 
                                          1024 * 1024, // maxResultBufferSize,
1163
 
                                          &configDictionary,
1164
 
                                          gEventQueue,
1165
 
                                          &(commandState->responseBody),
1166
 
                                          &(commandState->responseBodyLength),
1167
 
                                          &deleteResultValue);
1168
 
 
1169
 
         sendResponse = TRUE;
1170
 
         break;
1171
 
   } // switch
1172
 
 
1173
 
 
1174
 
abort:
1175
 
   if ((VIX_OK != err) || (sendResponse)) {
1176
 
      if (ForeignToolsIsCommandAlive(commandState)) {
1177
 
         ForeignToolsSendResponse(connectionState,
1178
 
                                &(connectionState->requestHeader),
1179
 
                                commandState->responseBodyLength,
1180
 
                                commandState->responseBody,
1181
 
                                err,
1182
 
                                additionalError,
1183
 
                                0); // responseFlags
1184
 
 
1185
 
         if (!deleteResultValue) {
1186
 
            commandState->responseBody = NULL;
1187
 
            commandState->responseBodyLength = 0;
1188
 
         }
1189
 
 
1190
 
         ForeignToolsDiscardCommand(commandState);
1191
 
      }
1192
 
   }
1193
 
} // ForeignToolsProcessMessage
1194
 
 
1195
 
 
1196
 
 
1197
 
 
1198
 
 
1199
 
 
1200
 
 
1201
 
 
1202