]> git.wh0rd.org - chrome-ext/crftp.git/blob - pnacl/ppapi.c
init
[chrome-ext/crftp.git] / pnacl / ppapi.c
1 // Written by Mike Frysinger <vapier@gmail.com>. Released into the public domain. Suck it.
2
3 #include <assert.h>
4 #include <inttypes.h>
5 #include <pthread.h>
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "ftplib/ftplib.h"
13
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/pp_module.h"
16 #include "ppapi/c/pp_var.h"
17 #include "ppapi/c/ppb.h"
18 #include "ppapi/c/ppb_console.h"
19 #include "ppapi/c/ppb_instance.h"
20 #include "ppapi/c/ppb_messaging.h"
21 #include "ppapi/c/ppb_url_loader.h"
22 #include "ppapi/c/ppb_url_request_info.h"
23 #include "ppapi/c/ppb_url_response_info.h"
24 #include "ppapi/c/ppb_var.h"
25 #include "ppapi/c/ppb_var_array.h"
26 #include "ppapi/c/ppp.h"
27 #include "ppapi/c/ppp_instance.h"
28 #include "ppapi/c/ppp_messaging.h"
29 #include "nacl_io/nacl_io.h"
30
31 typedef struct PP_Var PP_Var;
32 typedef struct PP_CompletionCallback PP_CompletionCallback;
33
34 static PP_Instance pp_instance = 0;
35 static PPB_GetInterface ppb_get_browser_interface = NULL;
36 static const PPB_Console *ppb_console_interface = NULL;
37 static const PPB_Messaging *ppb_messaging_interface = NULL;
38 static const PPB_URLLoader *ppb_urlloader_interface = NULL;
39 static const PPB_URLRequestInfo *ppb_urlrequestinfo_interface = NULL;
40 static const PPB_URLResponseInfo *ppb_urlresponseinfo_interface = NULL;
41 static const PPB_Var *ppb_var_interface = NULL;
42 static const PPB_VarArray *ppb_var_array_interface = NULL;
43
44 #include "util.h"
45 #include "queue.h"
46
47 /** A handle to the thread the handles messages. */
48 static pthread_t handle_message_thread;
49
50 static netbuf *pp_conn = NULL;
51
52 enum {
53 STATUS_DEBUG = 0,
54 STATUS_RESPONSE,
55 STATUS_CMD,
56 STATUS_ERROR,
57 STATUS_RELEASE,
58 };
59 __attribute__((format(printf, 2, 3)))
60 static void SendStatus(int32_t status, const char *format, ...)
61 {
62 va_list args;
63 PP_Var var_array, var_status, var_msg;
64 char *string;
65
66 va_start(args, format);
67 string = VprintfToNewString(format, args);
68 va_end(args);
69
70 var_status = PP_MakeInt32(status);
71 var_msg = ppb_var_interface->VarFromUtf8(string, strlen(string));
72 free(string);
73
74 var_array = ppb_var_array_interface->Create();
75 ppb_var_array_interface->Set(var_array, 0, var_status);
76 ppb_var_array_interface->Set(var_array, 1, var_msg);
77
78 ppb_messaging_interface->PostMessage(pp_instance, var_array);
79 ppb_var_interface->Release(var_msg);
80 ppb_var_interface->Release(var_array);
81 }
82
83 static int
84 ftplib_data_debug_callback(char *buf, int len)
85 {
86 SendStatus(STATUS_DEBUG, "%*s", len, buf);
87 return len;
88 }
89
90 static int
91 ftplib_data_response_callback(char *buf, int len)
92 {
93 SendStatus(STATUS_RESPONSE, "%*s", len, buf);
94 return len;
95 }
96
97 static int _FtpQuit(netbuf *conn)
98 {
99 /* Make sure to invalidate our handle since the core freed it. */
100 pp_conn = NULL;
101
102 /* First try a simple quit. If that fails, close it harder. */
103 return FtpQuit(conn) ? : FtpClose(conn);
104 }
105 static int _FtpDir(const char *path, netbuf *conn)
106 {
107 return FtpDir(ftplib_data_response_callback, path, conn);
108 }
109 static int _FtpNlst(const char *path, netbuf *conn)
110 {
111 return FtpNlst(ftplib_data_response_callback, path, conn);
112 }
113 static int _FtpPwd(netbuf *conn)
114 {
115 char b[1];
116 return FtpPwd(b, sizeof(b), conn);
117 }
118 static int _FtpSize(const char *path, netbuf *conn)
119 {
120 fsz_t size;
121 return FtpSizeLong(path, &size, FTPLIB_BINARY, conn);
122 }
123 static int _FtpSysType(netbuf *conn)
124 {
125 char b[1];
126 return FtpSysType(b, sizeof(b), conn);
127 }
128 static int _FtpCat(const char *path, netbuf *conn)
129 {
130 return FtpGet(ftplib_data_debug_callback, path, FTPLIB_ASCII, conn);
131 }
132
133 static int _FtpPut(const char *url, const char *name, netbuf *conn)
134 {
135 int32_t result;
136 int ret;
137 netbuf *data;
138 char buf[8192];
139
140 /* Start the FTP data connection. */
141 ret = FtpAccess(name, FTPLIB_FILE_WRITE, FTPLIB_BINARY, conn, &data);
142 if (!ret)
143 return ret;
144 SendStatus(STATUS_DEBUG, "Data connection established");
145
146 /* Set up the URL reader. */
147 PP_Resource url_rc = ppb_urlloader_interface->Create(pp_instance);
148
149 PP_Resource url_request_rc = ppb_urlrequestinfo_interface->Create(pp_instance);
150 PP_Var var_url = CStrToVar(url);
151 PP_Var var_method = CStrToVar("GET");
152 ppb_urlrequestinfo_interface->SetProperty(url_request_rc, PP_URLREQUESTPROPERTY_URL, var_url);
153 ppb_urlrequestinfo_interface->SetProperty(url_request_rc, PP_URLREQUESTPROPERTY_METHOD, var_method);
154
155 result = ppb_urlloader_interface->Open(url_rc, url_request_rc, PP_BlockUntilComplete());
156 if (result != PP_OK) {
157 SendStatus(STATUS_ERROR, "internal error: open url failed %s", url);
158 goto done;
159 }
160
161 /* Start streaming the data to the server. */
162 do {
163 result = ppb_urlloader_interface->ReadResponseBody(url_rc, buf, sizeof(buf), PP_BlockUntilComplete());
164 if (result > 0)
165 FtpWrite(buf, result, data);
166 } while (result > 0);
167 if (result != PP_OK)
168 SendStatus(STATUS_ERROR, "internal error: reading url failed %i", result);
169
170 done:
171 /* Break down the URL streamer and FTP connection. */
172 SendStatus(STATUS_RELEASE, "%s", url);
173 SendStatus(STATUS_DEBUG, "Data connection finished");
174 ppb_urlloader_interface->Close(url_rc);
175 ppb_var_interface->Release(var_method);
176 ppb_var_interface->Release(var_url);
177 ppb_urlloader_interface->Close(url_rc);
178
179 FtpClose(data);
180
181 return ret;
182 }
183
184 static int _FtpGet(const char *url, const char *name, netbuf *conn)
185 {
186 int32_t result;
187 int ret;
188 netbuf *data;
189 char buf[8192];
190
191 /* Start the FTP data connection. */
192 ret = FtpAccess(name, FTPLIB_FILE_READ, FTPLIB_BINARY, conn, &data);
193 if (!ret)
194 return ret;
195 SendStatus(STATUS_DEBUG, "Data connection established");
196
197 /* Set up the URL writer. */
198 PP_Resource url_rc = ppb_urlloader_interface->Create(pp_instance);
199
200 PP_Resource url_request_rc = ppb_urlrequestinfo_interface->Create(pp_instance);
201 PP_Var var_url = CStrToVar(url);
202 PP_Var var_method = CStrToVar("PUT");
203 ppb_urlrequestinfo_interface->SetProperty(url_request_rc, PP_URLREQUESTPROPERTY_URL, var_url);
204 ppb_urlrequestinfo_interface->SetProperty(url_request_rc, PP_URLREQUESTPROPERTY_METHOD, var_method);
205
206 /* Start streaming the data from the server. */
207 do {
208 ret = FtpRead(buf, sizeof(buf), data);
209 if (ret > 0) {
210 result = ppb_urlrequestinfo_interface->AppendDataToBody(url_request_rc, buf, ret);
211 if (result != PP_TRUE) {
212 SendStatus(STATUS_ERROR, "internal error: appending body failed %i", result);
213 break;
214 }
215 }
216 //SendStatus(STATUS_DEBUG, "ret = %i result = %i", ret, result);
217 } while (ret > 0);
218
219 /* Save the data to the URL. */
220 result = ppb_urlloader_interface->Open(url_rc, url_request_rc, PP_BlockUntilComplete());
221 if (result != PP_OK)
222 SendStatus(STATUS_ERROR, "internal error: open url failed %s", url);
223
224 /* Break down the URL streamer and FTP connection. */
225 SendStatus(STATUS_RELEASE, "%s", url);
226 SendStatus(STATUS_DEBUG, "Data connection finished");
227 ppb_urlloader_interface->Close(url_rc);
228 ppb_var_interface->Release(var_method);
229 ppb_var_interface->Release(var_url);
230 ppb_urlloader_interface->Close(url_rc);
231
232 FtpClose(data);
233
234 return ret;
235 }
236
237 struct cmd {
238 const char *cmd;
239 const char *usage;
240 uint32_t argc;
241 union {
242 int (*a0)(netbuf *);
243 int (*a1)(const char *, netbuf *);
244 int (*a2)(const char *, const char *, netbuf *);
245 } cb;
246 const char * const aliases[10];
247 };
248 #define A(n, f) n, .cb.a##n = f
249 static const struct cmd cmds[] = {
250 { "cat", "<path", A(1, _FtpCat), },
251 { "cdup", NULL, A(0, FtpCDUp), },
252 { "chdir", "<path>", A(1, FtpChdir),
253 {"cd"}, },
254 { "delete", "<path>", A(1, FtpDelete),
255 {"del",
256 "dele",
257 "rm",
258 "remove",
259 "unlink"}, },
260 { "dir", "<path>", A(1, _FtpDir), },
261 { "quit", NULL, A(0, _FtpQuit),
262 {"close",
263 "disconnect",
264 "exit"}, },
265 { "get", "<local> <remote>", A(2, _FtpGet), },
266 { "list", "<path>", A(1, _FtpNlst),
267 {"ls"}, },
268 { "login", "<user> <pass>", A(2, FtpLogin), },
269 { "mkdir", "<path>", A(1, FtpMkdir),
270 {"mkd"}, },
271 { "pwd", NULL, A(0, _FtpPwd),
272 {"cwd"}, },
273 { "put", "<local> <remote>", A(2, _FtpPut),
274 {"stor",
275 "store"}, },
276 { "rename", "<src> <dst>", A(2, FtpRename),
277 {"mv"} },
278 { "rmdir", "<path>", A(1, FtpRmdir),
279 {"rmd"}, },
280 { "site", "<cmd>", A(1, FtpSite), },
281 { "size", "<path>", A(1, _FtpSize), },
282 { "syst", NULL, A(0, _FtpSysType),
283 {"systyp",
284 "systype"}, },
285 };
286
287 static const struct cmd *lookup_cmd(const char *scmd)
288 {
289 size_t i;
290
291 for (i = 0; i < ARRAY_SIZE(cmds); ++i) {
292 const struct cmd *cmd = &cmds[i];
293 if (!strcmp(cmd->cmd, scmd))
294 return cmd;
295
296 const char * const *aliases = cmd->aliases;
297 size_t a;
298 for (a = 0; aliases[a]; ++a)
299 if (!strcmp(aliases[a], scmd))
300 return cmd;
301 }
302
303 return NULL;
304 }
305
306 static const struct cmd *check_cmd(const char *scmd, uint32_t argc)
307 {
308 const struct cmd *cmd = lookup_cmd(scmd);
309 if (!cmd)
310 return cmd;
311
312 if (cmd->argc != argc) {
313 SendStatus(STATUS_ERROR, "usage: %s %s", scmd, cmd->usage);
314 return NULL;
315 } else
316 return cmd;
317 }
318
319 static int
320 ftplib_response_callback(netbuf *conn, const char *resp)
321 {
322 SendStatus(STATUS_RESPONSE, "%s", resp);
323 return 0;
324 }
325
326 static int
327 ftplib_sendcmd_callback(netbuf *conn, const char *resp)
328 {
329 SendStatus(STATUS_CMD, "%s", resp);
330 return 0;
331 }
332
333 /* Process each message the main thread gave us. */
334 static void
335 HandleMessage(char **argv, uint32_t argc)
336 {
337 int ret;
338 uint32_t i;
339 const struct cmd *cmd;
340
341 console.log("DEBUG");
342 for (i = 0; i < argc; ++i)
343 console.log(argv[i] ? : "<NULL>");
344
345 /* Handle the connect command specially to deal with state. */
346 if (!strcmp(argv[0], "connect")) {
347 if (argc != 2) {
348 SendStatus(STATUS_ERROR, "usage: connect <host[:port]>");
349 return;
350 }
351
352 if (pp_conn)
353 FtpClose(pp_conn);
354
355 SendStatus(STATUS_DEBUG, "Connecting to %s ...", argv[1]);
356 if (!FtpConnect(argv[1], ftplib_response_callback, &pp_conn)) {
357 SendStatus(STATUS_ERROR, "Connection failed");
358 } else {
359 SendStatus(STATUS_DEBUG, "Connection established");
360 FtpSetSendCmdCallback(ftplib_sendcmd_callback, pp_conn);
361 }
362
363 return;
364 } else if (pp_conn == NULL) {
365 SendStatus(STATUS_ERROR, "Not connected!");
366 return;
367 }
368
369 cmd = check_cmd(argv[0], argc - 1);
370 if (cmd == NULL) {
371 SendStatus(STATUS_ERROR, "unknown command: %s", argv[0]);
372 return;
373 }
374
375 switch (cmd->argc) {
376 case 0: ret = cmd->cb.a0(pp_conn); break;
377 case 1: ret = cmd->cb.a1(argv[1], pp_conn); break;
378 case 2: ret = cmd->cb.a2(argv[1], argv[2], pp_conn); break;
379 default:
380 ret = 0;
381 console.log("unhandled arg count");
382 }
383 if (ret == 0)
384 SendStatus(STATUS_ERROR, "error %s", FtpLastResponse(pp_conn));
385 }
386
387 /* Background worker thread. */
388 static void *
389 HandleMessageThread(void *arg)
390 {
391 while (1) {
392 struct q_ele *msg = DequeueMessage();
393 HandleMessage(msg->argv, msg->argc);
394 CArrFree(msg->argv, msg->argc);
395 }
396 }
397
398 /* Callback when the <embed> is created. */
399 static PP_Bool
400 Instance_DidCreate(PP_Instance instance, uint32_t argc,
401 const char *argn[], const char *argv[])
402 {
403 pp_instance = instance;
404 nacl_io_init_ppapi(instance, ppb_get_browser_interface);
405
406 pthread_create(&handle_message_thread, NULL, &HandleMessageThread, NULL);
407 InitializeMessageQueue();
408
409 console.tip("CrFTP module created (written by Mike Frysinger <vapier@gmail.com>)");
410 extern char *version;
411 console.tip(version);
412
413 return PP_TRUE;
414 }
415
416 /* Callback when the instance is destroyed. */
417 static void
418 Instance_DidDestroy(PP_Instance instance)
419 {
420 if (pp_conn)
421 FtpClose(pp_conn);
422 pp_conn = NULL;
423 }
424
425 /* Callback when the <embed> changes. */
426 static void
427 Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource)
428 {}
429
430 /* Callback when the <embed> focus changes. */
431 static void
432 Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus)
433 {}
434
435 /* Callback when the document finishes loading. */
436 static PP_Bool
437 Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader)
438 {
439 /* NaCl modules do not need to handle the document load function. */
440 return PP_FALSE;
441 }
442
443 /* Callback when the JS sends us a message.
444 *
445 * We expect messages to be an array of commands for the ftplib core.
446 */
447 static void
448 Messaging_HandleMessage(PP_Instance instance, PP_Var message)
449 {
450 if (message.type != PP_VARTYPE_ARRAY) {
451 console.error("Invalid message received");
452 return;
453 }
454
455 char **argv;
456 uint32_t argc;
457 if (!VarToCArr(message, &argv, &argc)) {
458 console.error("Could not read message");
459 return;
460 }
461
462 if (argc == 0) {
463 console.log("Missing command");
464 CArrFree(argv, argc);
465 } else if (!EnqueueMessage(argv, argc)) {
466 SendStatus(STATUS_ERROR, "internal error: dropped message due to full queue");
467 CArrFree(argv, argc);
468 }
469 }
470
471 /* Callback to get pointers to the other module funcs. */
472 PP_EXPORT const void *
473 PPP_GetInterface(const char *interface_name)
474 {
475 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
476 static PPP_Instance instance_interface = {
477 &Instance_DidCreate,
478 &Instance_DidDestroy,
479 &Instance_DidChangeView,
480 &Instance_DidChangeFocus,
481 &Instance_HandleDocumentLoad,
482 };
483 return &instance_interface;
484 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
485 static PPP_Messaging messaging_interface = {
486 &Messaging_HandleMessage,
487 };
488 return &messaging_interface;
489 }
490 return NULL;
491 }
492
493 /* Callback to initialize the module. */
494 PP_EXPORT int32_t
495 PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser)
496 {
497 FtpInit();
498
499 ppb_get_browser_interface = get_browser;
500 ppb_console_interface = get_browser(PPB_CONSOLE_INTERFACE);
501 ppb_messaging_interface = get_browser(PPB_MESSAGING_INTERFACE);
502 ppb_var_interface = get_browser(PPB_VAR_INTERFACE);
503 ppb_var_array_interface = get_browser(PPB_VAR_ARRAY_INTERFACE);
504 ppb_urlloader_interface = get_browser(PPB_URLLOADER_INTERFACE);
505 ppb_urlrequestinfo_interface = get_browser(PPB_URLREQUESTINFO_INTERFACE);
506 ppb_urlresponseinfo_interface = get_browser(PPB_URLRESPONSEINFO_INTERFACE);
507
508 return PP_OK;
509 }
510
511 /* Callback before we shutdown the module. */
512 PP_EXPORT void
513 PPP_ShutdownModule(void)
514 {
515 }