1 #include <petscwebclient.h>
2 PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wdeprecated-declarations")
3
4 /*
5 Encodes and decodes from MIME Base64
6 */
7 static const char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
8 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
9
base64_encode(const unsigned char * data,unsigned char * encoded_data,size_t len)10 static PetscErrorCode base64_encode(const unsigned char *data, unsigned char *encoded_data, size_t len)
11 {
12 static size_t mod_table[] = {0, 2, 1};
13 size_t i, j;
14 size_t input_length, output_length;
15
16 PetscFunctionBegin;
17 PetscCall(PetscStrlen((const char *)data, &input_length));
18 output_length = 4 * ((input_length + 2) / 3);
19 PetscCheck(output_length <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Output length not large enough");
20
21 for (i = 0, j = 0; i < input_length;) {
22 uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
23 uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
24 uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
25 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
26
27 encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
28 encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
29 encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
30 encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
31 }
32 encoded_data[j] = 0;
33 for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[output_length - 1 - i] = '=';
34 PetscFunctionReturn(PETSC_SUCCESS);
35 }
36
base64_decode(const unsigned char * data,unsigned char * decoded_data,size_t length)37 PETSC_UNUSED static PetscErrorCode base64_decode(const unsigned char *data, unsigned char *decoded_data, size_t length)
38 {
39 static char decoding_table[257];
40 static int decode_table_built = 0;
41 size_t i, j;
42 size_t input_length, output_length;
43
44 PetscFunctionBegin;
45 if (!decode_table_built) {
46 for (i = 0; i < 64; i++) decoding_table[(unsigned char)encoding_table[i]] = i;
47 decode_table_built = 1;
48 }
49
50 PetscCall(PetscStrlen((const char *)data, &input_length));
51 PetscCheck(input_length % 4 == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input length must be divisible by 4");
52
53 output_length = input_length / 4 * 3;
54 if (data[input_length - 1] == '=') (output_length)--;
55 if (data[input_length - 2] == '=') (output_length)--;
56 PetscCheck(output_length <= length, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Output length too shore");
57
58 for (i = 0, j = 0; i < input_length;) {
59 uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
60 uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
61 uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
62 uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
63 uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
64
65 if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
66 if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
67 if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
68 }
69 decoded_data[j] = 0;
70 PetscFunctionReturn(PETSC_SUCCESS);
71 }
72
73 #if defined(PETSC_HAVE_UNISTD_H)
74 #include <unistd.h>
75 #endif
76
77 /*@C
78 PetscGlobusAuthorize - Get an access token allowing PETSc applications to make Globus file transfer requests
79
80 Not Collective, only the first process in `MPI_Comm` does anything
81
82 Input Parameters:
83 + comm - the MPI communicator
84 - tokensize - size of the token array
85
86 Output Parameter:
87 . access_token - can be used with `PetscGlobusUpLoad()` for 30 days
88
89 Level: intermediate
90
91 Notes:
92 This call requires `stdout` and `stdin` access from process 0 on the MPI communicator
93
94 You can run src/sys/webclient/tutorials/globusobtainaccesstoken to get an access token
95
96 .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscGlobusUpload()`
97 @*/
PetscGlobusAuthorize(MPI_Comm comm,char access_token[],size_t tokensize)98 PetscErrorCode PetscGlobusAuthorize(MPI_Comm comm, char access_token[], size_t tokensize)
99 {
100 SSL_CTX *ctx;
101 SSL *ssl;
102 int sock;
103 char buff[8 * 1024], *ptr, head[1024];
104 PetscMPIInt rank;
105 size_t len;
106 PetscBool found;
107
108 PetscFunctionBegin;
109 PetscCallMPI(MPI_Comm_rank(comm, &rank));
110 if (rank == 0) {
111 PetscCheck(isatty(fileno(PETSC_STDOUT)), PETSC_COMM_SELF, PETSC_ERR_USER, "Requires users input/output");
112 PetscCall(PetscPrintf(comm, "Enter globus username:"));
113 ptr = fgets(buff, 1024, stdin);
114 PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
115 PetscCall(PetscStrlen(buff, &len));
116 buff[len - 1] = ':'; /* remove carriage return at end of line */
117
118 PetscCall(PetscPrintf(comm, "Enter globus password:"));
119 ptr = fgets(buff + len, 1024 - len, stdin);
120 PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
121 PetscCall(PetscStrlen(buff, &len));
122 buff[len - 1] = '\0'; /* remove carriage return at end of line */
123 PetscCall(PetscStrncpy(head, "Authorization: Basic ", sizeof(head)));
124 PetscCall(base64_encode((const unsigned char *)buff, (unsigned char *)(head + 21), sizeof(head) - 21));
125 PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));
126
127 PetscCall(PetscSSLInitializeContext(&ctx));
128 PetscCall(PetscHTTPSConnect("nexus.api.globusonline.org", 443, ctx, &sock, &ssl));
129 PetscCall(PetscHTTPSRequest("GET", "nexus.api.globusonline.org/goauth/token?grant_type=client_credentials", head, "application/x-www-form-urlencoded", NULL, ssl, buff, sizeof(buff)));
130 PetscCall(PetscSSLDestroyContext(ctx));
131 close(sock);
132
133 PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found));
134 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return access token");
135
136 PetscCall(PetscPrintf(comm, "Here is your Globus access token, save it in a save place, in the future you can run PETSc\n"));
137 PetscCall(PetscPrintf(comm, "programs with the option -globus_access_token %s\n", access_token));
138 PetscCall(PetscPrintf(comm, "to access Globus automatically\n"));
139 }
140 PetscFunctionReturn(PETSC_SUCCESS);
141 }
142
143 /*@C
144 PetscGlobusGetTransfers - Get a record of current transfers requested from Globus
145
146 Not Collective, only the first process in `MPI_Comm` does anything
147
148 Input Parameters:
149 + comm - the MPI communicator
150 . access_token - Globus access token, if `NULL` will check in options database for -globus_access_token XXX otherwise
151 will call `PetscGlobusAuthorize()`.
152 - buffsize - size of the buffer
153
154 Output Parameter:
155 . buff - location to put Globus information
156
157 Level: intermediate
158
159 .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscGlobusUpload()`, `PetscGlobusAuthorize()`
160 @*/
PetscGlobusGetTransfers(MPI_Comm comm,const char access_token[],char buff[],size_t buffsize)161 PetscErrorCode PetscGlobusGetTransfers(MPI_Comm comm, const char access_token[], char buff[], size_t buffsize)
162 {
163 SSL_CTX *ctx;
164 SSL *ssl;
165 int sock;
166 char head[4096];
167 PetscMPIInt rank;
168
169 PetscFunctionBegin;
170 PetscCallMPI(MPI_Comm_rank(comm, &rank));
171 if (rank == 0) {
172 PetscCall(PetscStrncpy(head, "Authorization : Globus-Goauthtoken ", sizeof(head)));
173 if (access_token) {
174 PetscCall(PetscStrlcat(head, access_token, sizeof(head)));
175 } else {
176 PetscBool set;
177 char accesstoken[4096];
178 PetscCall(PetscOptionsGetString(NULL, NULL, "-globus_access_token", accesstoken, sizeof(accesstoken), &set));
179 PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_USER, "Pass in Globus accesstoken or use -globus_access_token XXX");
180 PetscCall(PetscStrlcat(head, accesstoken, sizeof(head)));
181 }
182 PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));
183
184 PetscCall(PetscSSLInitializeContext(&ctx));
185 PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
186 PetscCall(PetscHTTPSRequest("GET", "transfer.api.globusonline.org/v0.10/tasksummary", head, "application/json", NULL, ssl, buff, buffsize));
187 PetscCall(PetscSSLDestroyContext(ctx));
188 close(sock);
189 }
190 PetscFunctionReturn(PETSC_SUCCESS);
191 }
192
193 /*@C
194 PetscGlobusUpload - Loads a file to Globus
195
196 Not Collective, only the first process in the `MPI_Comm` uploads the file
197
198 Input Parameters:
199 + comm - MPI communicator
200 . access_token - obtained with `PetscGlobusAuthorize()`, pass `NULL` to use `-globus_access_token XXX` from the PETSc database
201 - filename - file to upload
202
203 Options Database Key:
204 . -globus_access_token XXX - the Globus token
205
206 Level: intermediate
207
208 .seealso: `PetscGoogleDriveAuthorize()`, `PetscGoogleDriveRefresh()`, `PetscGlobusAuthorize()`
209 @*/
PetscGlobusUpload(MPI_Comm comm,const char access_token[],const char filename[])210 PetscErrorCode PetscGlobusUpload(MPI_Comm comm, const char access_token[], const char filename[])
211 {
212 SSL_CTX *ctx;
213 SSL *ssl;
214 int sock;
215 char head[4096], buff[8 * 1024], body[4096], submission_id[4096];
216 PetscMPIInt rank;
217 PetscBool flg, found;
218
219 PetscFunctionBegin;
220 PetscCallMPI(MPI_Comm_rank(comm, &rank));
221 if (rank == 0) {
222 PetscCall(PetscTestFile(filename, 'r', &flg));
223 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to find file: %s", filename);
224
225 PetscCall(PetscStrncpy(head, "Authorization : Globus-Goauthtoken ", sizeof(head)));
226 if (access_token) {
227 PetscCall(PetscStrlcat(head, access_token, sizeof(head)));
228 } else {
229 PetscBool set;
230 char accesstoken[4096];
231 PetscCall(PetscOptionsGetString(NULL, NULL, "-globus_access_token", accesstoken, sizeof(accesstoken), &set));
232 PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_USER, "Pass in Globus accesstoken or use -globus_access_token XXX");
233 PetscCall(PetscStrlcat(head, accesstoken, sizeof(head)));
234 }
235 PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));
236
237 /* Get Globus submission id */
238 PetscCall(PetscSSLInitializeContext(&ctx));
239 PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
240 PetscCall(PetscHTTPSRequest("GET", "transfer.api.globusonline.org/v0.10/submission_id", head, "application/json", NULL, ssl, buff, sizeof(buff)));
241 PetscCall(PetscSSLDestroyContext(ctx));
242 close(sock);
243 PetscCall(PetscPullJSONValue(buff, "value", submission_id, sizeof(submission_id), &found));
244 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return submission id");
245
246 /* build JSON body of transfer request */
247 PetscCall(PetscStrncpy(body, "{", sizeof(body)));
248 PetscCall(PetscPushJSONValue(body, "submission_id", submission_id, sizeof(body)));
249 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
250 PetscCall(PetscPushJSONValue(body, "DATA_TYPE", "transfer", sizeof(body)));
251 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
252 PetscCall(PetscPushJSONValue(body, "sync_level", "null", sizeof(body)));
253 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
254 PetscCall(PetscPushJSONValue(body, "source_endpoint", "barryfsmith#MacBookPro", sizeof(body)));
255 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
256 PetscCall(PetscPushJSONValue(body, "label", "PETSc transfer label", sizeof(body)));
257 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
258 PetscCall(PetscPushJSONValue(body, "length", "1", sizeof(body)));
259 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
260 PetscCall(PetscPushJSONValue(body, "destination_endpoint", "mcs#home", sizeof(body)));
261 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
262
263 PetscCall(PetscStrlcat(body, "\"DATA\": [ {", sizeof(body)));
264 PetscCall(PetscPushJSONValue(body, "source_path", "/~/FEM_GPU.pdf", sizeof(body)));
265 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
266 PetscCall(PetscPushJSONValue(body, "destination_path", "/~/FEM_GPU.pdf", sizeof(body)));
267 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
268 PetscCall(PetscPushJSONValue(body, "verify_size", "null", sizeof(body)));
269 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
270 PetscCall(PetscPushJSONValue(body, "recursive", "false", sizeof(body)));
271 PetscCall(PetscStrlcat(body, ",", sizeof(body)));
272 PetscCall(PetscPushJSONValue(body, "DATA_TYPE", "transfer_item", sizeof(body)));
273 PetscCall(PetscStrlcat(body, "} ] }", sizeof(body)));
274
275 PetscCall(PetscSSLInitializeContext(&ctx));
276 PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
277 PetscCall(PetscHTTPSRequest("POST", "transfer.api.globusonline.org/v0.10/transfer", head, "application/json", body, ssl, buff, sizeof(buff)));
278 PetscCall(PetscSSLDestroyContext(ctx));
279 close(sock);
280 PetscCall(PetscPullJSONValue(buff, "code", submission_id, sizeof(submission_id), &found));
281 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return code on transfer");
282 PetscCall(PetscStrcmp(submission_id, "Accepted", &found));
283 PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not accept transfer");
284 }
285 PetscFunctionReturn(PETSC_SUCCESS);
286 }
287