1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=8:
3  */
4 /* vim:set ts=8 sw=2 et cindent: */
5 /* This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <strings.h>
12 #include <unistd.h>
13 #include <limits.h>
14 
15 #include "RemoteUtils.h"
16 
17 #ifdef IS_BIG_ENDIAN
18 #  define TO_LITTLE_ENDIAN32(x)                           \
19     ((((x)&0xff000000) >> 24) | (((x)&0x00ff0000) >> 8) | \
20      (((x)&0x0000ff00) << 8) | (((x)&0x000000ff) << 24))
21 #else
22 #  define TO_LITTLE_ENDIAN32(x) (x)
23 #endif
24 
25 #ifndef MAX_PATH
26 #  ifdef PATH_MAX
27 #    define MAX_PATH PATH_MAX
28 #  else
29 #    define MAX_PATH 1024
30 #  endif
31 #endif
32 
33 /* like strcpy, but return the char after the final null */
estrcpy(const char * s,char * d)34 static char* estrcpy(const char* s, char* d) {
35   while (*s) *d++ = *s++;
36 
37   *d++ = '\0';
38   return d;
39 }
40 
41 /* Construct a command line from given args and desktop startup ID.
42  * Returned buffer must be released by free().
43  */
ConstructCommandLine(int32_t argc,char ** argv,const char * aDesktopStartupID,int * aCommandLineLength)44 char* ConstructCommandLine(int32_t argc, char** argv,
45                            const char* aDesktopStartupID,
46                            int* aCommandLineLength) {
47   char cwdbuf[MAX_PATH];
48   if (!getcwd(cwdbuf, MAX_PATH)) return nullptr;
49 
50   // the commandline property is constructed as an array of int32_t
51   // followed by a series of null-terminated strings:
52   //
53   // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
54   // (offset is from the beginning of the buffer)
55 
56   static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID=";
57 
58   int32_t argvlen = strlen(cwdbuf);
59   for (int i = 0; i < argc; ++i) {
60     int32_t len = strlen(argv[i]);
61     if (i == 0 && aDesktopStartupID) {
62       len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
63     }
64     argvlen += len;
65   }
66 
67   auto* buffer =
68       (int32_t*)malloc(argvlen + argc + 1 + sizeof(int32_t) * (argc + 1));
69   if (!buffer) return nullptr;
70 
71   buffer[0] = TO_LITTLE_ENDIAN32(argc);
72 
73   auto* bufend = (char*)(buffer + argc + 1);
74 
75   bufend = estrcpy(cwdbuf, bufend);
76 
77   for (int i = 0; i < argc; ++i) {
78     buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*)buffer));
79     bufend = estrcpy(argv[i], bufend);
80     if (i == 0 && aDesktopStartupID) {
81       bufend = estrcpy(desktopStartupPrefix, bufend - 1);
82       bufend = estrcpy(aDesktopStartupID, bufend - 1);
83     }
84   }
85 
86 #ifdef DEBUG_command_line
87   int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer);
88   char* debug_workingdir = (char*)(buffer + argc + 1);
89 
90   printf(
91       "Sending command line:\n"
92       "  working dir: %s\n"
93       "  argc:\t%i",
94       debug_workingdir, debug_argc);
95 
96   int32_t* debug_offset = buffer + 1;
97   for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
98     printf("  argv[%i]:\t%s\n", debug_i,
99            ((char*)buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
100 #endif
101 
102   *aCommandLineLength = bufend - reinterpret_cast<char*>(buffer);
103   return reinterpret_cast<char*>(buffer);
104 }
105