1 // Written by Mike Frysinger <vapier@gmail.com>. Released into the public domain. Suck it.
12 #include "ftplib/ftplib.h"
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"
31 typedef struct PP_Var PP_Var
;
32 typedef struct PP_CompletionCallback PP_CompletionCallback
;
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
;
47 /** A handle to the thread the handles messages. */
48 static pthread_t handle_message_thread
;
50 static netbuf
*pp_conn
= NULL
;
59 __attribute__((format(printf
, 2, 3)))
60 static void SendStatus(int32_t status
, const char *format
, ...)
63 PP_Var var_array
, var_status
, var_msg
;
66 va_start(args
, format
);
67 string
= VprintfToNewString(format
, args
);
70 var_status
= PP_MakeInt32(status
);
71 var_msg
= ppb_var_interface
->VarFromUtf8(string
, strlen(string
));
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
);
78 ppb_messaging_interface
->PostMessage(pp_instance
, var_array
);
79 ppb_var_interface
->Release(var_msg
);
80 ppb_var_interface
->Release(var_array
);
84 ftplib_data_debug_callback(char *buf
, int len
)
86 SendStatus(STATUS_DEBUG
, "%*s", len
, buf
);
91 ftplib_data_response_callback(char *buf
, int len
)
93 SendStatus(STATUS_RESPONSE
, "%*s", len
, buf
);
97 static int _FtpQuit(netbuf
*conn
)
99 /* Make sure to invalidate our handle since the core freed it. */
102 /* First try a simple quit. If that fails, close it harder. */
103 return FtpQuit(conn
) ? : FtpClose(conn
);
105 static int _FtpDir(const char *path
, netbuf
*conn
)
107 return FtpDir(ftplib_data_response_callback
, path
, conn
);
109 static int _FtpNlst(const char *path
, netbuf
*conn
)
111 return FtpNlst(ftplib_data_response_callback
, path
, conn
);
113 static int _FtpPwd(netbuf
*conn
)
116 return FtpPwd(b
, sizeof(b
), conn
);
118 static int _FtpSize(const char *path
, netbuf
*conn
)
121 return FtpSizeLong(path
, &size
, FTPLIB_BINARY
, conn
);
123 static int _FtpSysType(netbuf
*conn
)
126 return FtpSysType(b
, sizeof(b
), conn
);
128 static int _FtpCat(const char *path
, netbuf
*conn
)
130 return FtpGet(ftplib_data_debug_callback
, path
, FTPLIB_ASCII
, conn
);
133 static int _FtpPut(const char *url
, const char *name
, netbuf
*conn
)
140 /* Start the FTP data connection. */
141 ret
= FtpAccess(name
, FTPLIB_FILE_WRITE
, FTPLIB_BINARY
, conn
, &data
);
144 SendStatus(STATUS_DEBUG
, "Data connection established");
146 /* Set up the URL reader. */
147 PP_Resource url_rc
= ppb_urlloader_interface
->Create(pp_instance
);
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
);
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
);
161 /* Start streaming the data to the server. */
163 result
= ppb_urlloader_interface
->ReadResponseBody(url_rc
, buf
, sizeof(buf
), PP_BlockUntilComplete());
165 FtpWrite(buf
, result
, data
);
166 } while (result
> 0);
168 SendStatus(STATUS_ERROR
, "internal error: reading url failed %i", result
);
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
);
184 static int _FtpGet(const char *url
, const char *name
, netbuf
*conn
)
191 /* Start the FTP data connection. */
192 ret
= FtpAccess(name
, FTPLIB_FILE_READ
, FTPLIB_BINARY
, conn
, &data
);
195 SendStatus(STATUS_DEBUG
, "Data connection established");
197 /* Set up the URL writer. */
198 PP_Resource url_rc
= ppb_urlloader_interface
->Create(pp_instance
);
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
);
206 /* Start streaming the data from the server. */
208 ret
= FtpRead(buf
, sizeof(buf
), data
);
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
);
216 //SendStatus(STATUS_DEBUG, "ret = %i result = %i", ret, result);
219 /* Save the data to the URL. */
220 result
= ppb_urlloader_interface
->Open(url_rc
, url_request_rc
, PP_BlockUntilComplete());
222 SendStatus(STATUS_ERROR
, "internal error: open url failed %s", url
);
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
);
243 int (*a1
)(const char *, netbuf
*);
244 int (*a2
)(const char *, const char *, netbuf
*);
246 const char * const aliases
[10];
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
),
254 { "delete", "<path>", A(1, FtpDelete
),
260 { "dir", "<path>", A(1, _FtpDir
), },
261 { "quit", NULL
, A(0, _FtpQuit
),
265 { "get", "<local> <remote>", A(2, _FtpGet
), },
266 { "list", "<path>", A(1, _FtpNlst
),
268 { "login", "<user> <pass>", A(2, FtpLogin
), },
269 { "mkdir", "<path>", A(1, FtpMkdir
),
271 { "pwd", NULL
, A(0, _FtpPwd
),
273 { "put", "<local> <remote>", A(2, _FtpPut
),
276 { "rename", "<src> <dst>", A(2, FtpRename
),
278 { "rmdir", "<path>", A(1, FtpRmdir
),
280 { "site", "<cmd>", A(1, FtpSite
), },
281 { "size", "<path>", A(1, _FtpSize
), },
282 { "syst", NULL
, A(0, _FtpSysType
),
287 static const struct cmd
*lookup_cmd(const char *scmd
)
291 for (i
= 0; i
< ARRAY_SIZE(cmds
); ++i
) {
292 const struct cmd
*cmd
= &cmds
[i
];
293 if (!strcmp(cmd
->cmd
, scmd
))
296 const char * const *aliases
= cmd
->aliases
;
298 for (a
= 0; aliases
[a
]; ++a
)
299 if (!strcmp(aliases
[a
], scmd
))
306 static const struct cmd
*check_cmd(const char *scmd
, uint32_t argc
)
308 const struct cmd
*cmd
= lookup_cmd(scmd
);
312 if (cmd
->argc
!= argc
) {
313 SendStatus(STATUS_ERROR
, "usage: %s %s", scmd
, cmd
->usage
);
320 ftplib_response_callback(netbuf
*conn
, const char *resp
)
322 SendStatus(STATUS_RESPONSE
, "%s", resp
);
327 ftplib_sendcmd_callback(netbuf
*conn
, const char *resp
)
329 SendStatus(STATUS_CMD
, "%s", resp
);
333 /* Process each message the main thread gave us. */
335 HandleMessage(char **argv
, uint32_t argc
)
339 const struct cmd
*cmd
;
341 console
.log("DEBUG");
342 for (i
= 0; i
< argc
; ++i
)
343 console
.log(argv
[i
] ? : "<NULL>");
345 /* Handle the connect command specially to deal with state. */
346 if (!strcmp(argv
[0], "connect")) {
348 SendStatus(STATUS_ERROR
, "usage: connect <host[:port]>");
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");
359 SendStatus(STATUS_DEBUG
, "Connection established");
360 FtpSetSendCmdCallback(ftplib_sendcmd_callback
, pp_conn
);
364 } else if (pp_conn
== NULL
) {
365 SendStatus(STATUS_ERROR
, "Not connected!");
369 cmd
= check_cmd(argv
[0], argc
- 1);
371 SendStatus(STATUS_ERROR
, "unknown command: %s", argv
[0]);
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;
381 console
.log("unhandled arg count");
384 SendStatus(STATUS_ERROR
, "error %s", FtpLastResponse(pp_conn
));
387 /* Background worker thread. */
389 HandleMessageThread(void *arg
)
392 struct q_ele
*msg
= DequeueMessage();
393 HandleMessage(msg
->argv
, msg
->argc
);
394 CArrFree(msg
->argv
, msg
->argc
);
398 /* Callback when the <embed> is created. */
400 Instance_DidCreate(PP_Instance instance
, uint32_t argc
,
401 const char *argn
[], const char *argv
[])
403 pp_instance
= instance
;
404 nacl_io_init_ppapi(instance
, ppb_get_browser_interface
);
406 pthread_create(&handle_message_thread
, NULL
, &HandleMessageThread
, NULL
);
407 InitializeMessageQueue();
409 console
.tip("CrFTP module created (written by Mike Frysinger <vapier@gmail.com>)");
410 extern char *version
;
411 console
.tip(version
);
416 /* Callback when the instance is destroyed. */
418 Instance_DidDestroy(PP_Instance instance
)
425 /* Callback when the <embed> changes. */
427 Instance_DidChangeView(PP_Instance instance
, PP_Resource view_resource
)
430 /* Callback when the <embed> focus changes. */
432 Instance_DidChangeFocus(PP_Instance instance
, PP_Bool has_focus
)
435 /* Callback when the document finishes loading. */
437 Instance_HandleDocumentLoad(PP_Instance instance
, PP_Resource url_loader
)
439 /* NaCl modules do not need to handle the document load function. */
443 /* Callback when the JS sends us a message.
445 * We expect messages to be an array of commands for the ftplib core.
448 Messaging_HandleMessage(PP_Instance instance
, PP_Var message
)
450 if (message
.type
!= PP_VARTYPE_ARRAY
) {
451 console
.error("Invalid message received");
457 if (!VarToCArr(message
, &argv
, &argc
)) {
458 console
.error("Could not read message");
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
);
471 /* Callback to get pointers to the other module funcs. */
472 PP_EXPORT
const void *
473 PPP_GetInterface(const char *interface_name
)
475 if (strcmp(interface_name
, PPP_INSTANCE_INTERFACE
) == 0) {
476 static PPP_Instance instance_interface
= {
478 &Instance_DidDestroy
,
479 &Instance_DidChangeView
,
480 &Instance_DidChangeFocus
,
481 &Instance_HandleDocumentLoad
,
483 return &instance_interface
;
484 } else if (strcmp(interface_name
, PPP_MESSAGING_INTERFACE
) == 0) {
485 static PPP_Messaging messaging_interface
= {
486 &Messaging_HandleMessage
,
488 return &messaging_interface
;
493 /* Callback to initialize the module. */
495 PPP_InitializeModule(PP_Module a_module_id
, PPB_GetInterface get_browser
)
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
);
511 /* Callback before we shutdown the module. */
513 PPP_ShutdownModule(void)