11 #include <npruntime.h>
13 #include "native_client/intermodule_comm/nacl_imc_c.h"
14 #include "native_client/service_runtime/include/sys/fcntl.h"
15 #include "native_client/service_runtime/nacl_desc_base.h"
16 #include "native_client/service_runtime/nacl_desc_imc.h"
17 #include "native_client/service_runtime/nacl_desc_io.h"
18 #include "native_client/service_runtime/nrd_xfer_lib/nrd_all_modules.h"
19 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer.h"
20 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer_effector.h"
26 static NPNetscapeFuncs g_funcs
;
27 static NPIdentifier ident_launch
;
28 static NPIdentifier ident_get_file
;
29 static NPIdentifier ident_send
;
30 static NPIdentifier ident_length
;
32 #define NPN_CreateObject (g_funcs.createobject)
33 #define NPN_RetainObject (g_funcs.retainobject)
34 #define NPN_ReleaseObject (g_funcs.releaseobject)
35 #define NPN_ReleaseVariantValue (g_funcs.releasevariantvalue)
36 #define NPN_InvokeDefault (g_funcs.invokeDefault)
37 #define NPN_GetProperty (g_funcs.getproperty)
38 #define NPN_UTF8FromIdentifier (g_funcs.utf8fromidentifier)
39 #define NPN_GetStringIdentifier (g_funcs.getstringidentifier)
40 #define NPN_GetIntIdentifier (g_funcs.getintidentifier)
41 #define NPN_GetURLNotify (g_funcs.geturlnotify)
42 #define NPN_PluginThreadAsyncCall (g_funcs.pluginthreadasynccall)
45 static void PrintIdentifier(NPIdentifier ident
)
47 char *str
= NPN_UTF8FromIdentifier(ident
);
48 print((" ident = \"%s\"\n", str
));
53 IsType(NPObject
*npobj
, NPClass
*npclass
)
55 /* The underscore in "_class" suggests this member is not part of
56 the public API, but it could just be to prevent a clash with the
58 return npobj
->_class
== npclass
;
62 /* Mozilla does not provide default behaviour for these NoOp_* methods
63 if NULL is provided instead in the NPClass. */
66 NoOp_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
72 NoOp_HasProperty(NPObject
*obj
, NPIdentifier propertyName
)
74 print(("Obj_HasProperty\n"));
75 PrintIdentifier(propertyName
);
80 NoOp_GetProperty(NPObject
*obj
, NPIdentifier propertyName
, NPVariant
*result
)
82 print(("Obj_GetProperty\n"));
88 struct NPObject header
;
89 struct NaClDesc
*desc
;
90 /* We keep a filename to pass to sel_ldr, but there's a chance the
91 file could be deleted from the cache.
92 TODO: pass FDs to sel_ldr instead. */
97 FileObj_Allocate(NPP npp
, NPClass
*npclass
)
99 print(("Obj_Allocate\n"));
100 struct FileObj
*obj
= malloc(sizeof(struct FileObj
));
105 FileObj_Deallocate(NPObject
*npobj
)
107 struct FileObj
*obj
= (struct FileObj
*) npobj
;
108 NaClDescUnref(obj
->desc
);
111 print(("FileObj_Deallocate\n"));
114 static NPClass FileObj_Vtable
= {
115 .structVersion
= NP_CLASS_STRUCT_VERSION
,
116 .allocate
= FileObj_Allocate
,
117 .deallocate
= FileObj_Deallocate
,
119 .hasMethod
= NoOp_HasMethod
,
121 .invokeDefault
= NULL
,
122 .hasProperty
= NoOp_HasProperty
,
123 .getProperty
= NoOp_GetProperty
,
125 .removeProperty
= NULL
,
130 struct NPObject header
;
132 struct NaClDesc
*desc
;
136 SendObj_Allocate(NPP npp
, NPClass
*npclass
)
138 print(("Obj_Allocate\n"));
139 struct SendObj
*obj
= malloc(sizeof(struct SendObj
));
145 SendObj_Deallocate(NPObject
*npobj
)
147 struct SendObj
*obj
= (struct SendObj
*) npobj
;
148 NaClDescUnref(obj
->desc
);
150 print(("SendObj_Deallocate\n"));
154 SendObj_HasMethod(NPObject
* npobj
, NPIdentifier methodName
)
156 return methodName
== ident_send
;
160 ConvertDescArray(NPP plugin
, NPObject
*array
, struct NaClImcTypedMsgHdr
*header
)
163 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
164 !NPVARIANT_IS_INT32(length_np
)) {
165 NPN_ReleaseVariantValue(&length_np
);
168 int length
= NPVARIANT_TO_INT32(length_np
);
169 NPN_ReleaseVariantValue(&length_np
);
171 /* Firstly, there will be a limit to the number of capabilities we
172 can send in a message. Secondly, the array is not necessarily a
173 primitive Javascript array; its .length property could be much
174 larger than the amount of space Javascript is normally allowed to
179 struct NaClDesc
**descs
= malloc(sizeof(struct NaClDesc
*) * length
);
183 for(i
= 0; i
< length
; i
++) {
184 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
186 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
187 !NPVARIANT_IS_OBJECT(element
))
188 return false; /* TODO: clear up */
189 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(element
);
190 if(!IsType(file_npobj
, &FileObj_Vtable
))
191 return false; /* TODO: clear up */
192 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
193 NaClDescRef(file_obj
->desc
);
194 descs
[i
] = file_obj
->desc
;
195 NPN_ReleaseVariantValue(&element
);
197 header
->ndescv
= descs
;
198 header
->ndesc_length
= length
;
203 SendObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
204 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
206 struct SendObj
*obj
= (struct SendObj
*) npobj
;
207 print(("Obj_Invoke %i\n", argCount
));
208 PrintIdentifier(methodName
);
209 if(methodName
== ident_send
) {
211 NPVARIANT_IS_STRING(args
[0]) &&
212 NPVARIANT_IS_OBJECT(args
[1])) {
213 NPString message_data
= NPVARIANT_TO_STRING(args
[0]);
214 NPObject
*caps_array
= NPVARIANT_TO_OBJECT(args
[1]);
215 struct NaClNrdXferEffector effector
;
216 if(!NaClNrdXferEffectorCtor(&effector
, obj
->desc
))
218 struct NaClImcMsgIoVec iovec
;
219 struct NaClImcTypedMsgHdr header
;
220 if(!ConvertDescArray(obj
->plugin
, caps_array
, &header
))
222 /* TODO: allow sending any data, not just UTF-8 */
223 iovec
.base
= (char *) message_data
.utf8characters
;
224 iovec
.length
= message_data
.utf8length
;
226 header
.iov_length
= 1;
228 NaClImcSendTypedMessage(obj
->desc
, (struct NaClDescEffector
*) &effector
,
231 for(i
= 0; i
< header
.ndesc_length
; i
++)
232 NaClDescUnref(header
.ndescv
[i
]);
234 effector
.base
.vtbl
->Dtor(&effector
.base
);
236 VOID_TO_NPVARIANT(*result
);
242 static NPClass SendObj_Vtable
= {
243 .structVersion
= NP_CLASS_STRUCT_VERSION
,
244 .allocate
= SendObj_Allocate
,
245 .deallocate
= SendObj_Deallocate
,
247 .hasMethod
= SendObj_HasMethod
,
248 .invoke
= SendObj_Invoke
,
249 .invokeDefault
= NULL
,
250 .hasProperty
= NoOp_HasProperty
,
251 .getProperty
= NoOp_GetProperty
,
253 .removeProperty
= NULL
,
258 struct NPObject header
;
263 LauncherObj_Allocate(NPP npp
, NPClass
*npclass
)
265 print(("Obj_Allocate\n"));
266 struct LauncherObj
*obj
= malloc(sizeof(struct LauncherObj
));
272 LauncherObj_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
274 print(("Obj_HasMethod %i\n", methodName
== ident_launch
));
275 PrintIdentifier(methodName
);
276 return (methodName
== ident_launch
||
277 methodName
== ident_get_file
);
280 struct ReceiveCallbackArgs
{
288 DoReceiveCallback(void *handle
)
290 struct ReceiveCallbackArgs
*call
= handle
;
293 /* TODO: don't assume this data is UTF-8 */
294 STRINGN_TO_NPVARIANT(call
->buffer
, call
->size
, args
[0]);
295 NPN_InvokeDefault(call
->plugin
, call
->callback
, args
, 1, &result
);
296 NPN_ReleaseVariantValue(&result
);
298 /* We don't do NPN_ReleaseObject() on the callback here because we
299 couldn't do NPN_RetainObject() earlier. */
302 struct ReceiveThreadArgs
{
305 struct NaClDesc
*desc
;
309 ReceiveThread(void *handle
)
311 /* We create one thread for every socket we listen on. It would be
312 more efficient to have one thread using poll(). NPAPI doesn't
313 provide a way for us to share the browser's event loop. */
314 struct ReceiveThreadArgs
*args
= handle
;
315 struct NaClNrdXferEffector effector
;
316 if(!NaClNrdXferEffectorCtor(&effector
, args
->desc
))
319 struct ReceiveCallbackArgs
*call
=
320 malloc(sizeof(struct ReceiveCallbackArgs
));
323 struct NaClImcMsgIoVec iovec
;
324 struct NaClImcTypedMsgHdr header
;
325 iovec
.base
= call
->buffer
;
326 iovec
.length
= sizeof(call
->buffer
);
328 header
.iov_length
= 1;
329 header
.ndescv
= NULL
;
330 header
.ndesc_length
= 0;
333 NaClImcRecvTypedMessage(args
->desc
, (struct NaClDescEffector
*) &effector
,
339 /* We can't call NPN_RetainObject() here because it is not safe to
340 call it from this thread. We assume that
341 NPN_PluginThreadAsyncCall() schedules functions to be called in
342 order, so that the function is not freed until later. */
343 call
->plugin
= args
->plugin
;
344 call
->callback
= args
->callback
;
345 call
->size
= got_bytes
;
346 NPN_PluginThreadAsyncCall(args
->plugin
, DoReceiveCallback
, call
);
348 effector
.base
.vtbl
->Dtor(&effector
.base
);
349 NaClDescUnref(args
->desc
); /* Supposed to be thread safe */
350 NPN_PluginThreadAsyncCall(args
->plugin
, (void (*)(void *)) NPN_ReleaseObject
,
357 ConvertArgvArray(NPP plugin
, NPObject
*array
,
358 char ***result_argv
, int *result_size
)
361 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
362 !NPVARIANT_IS_INT32(length_np
)) {
363 NPN_ReleaseVariantValue(&length_np
);
366 int length
= NPVARIANT_TO_INT32(length_np
);
367 NPN_ReleaseVariantValue(&length_np
);
369 /* Arbitrary limit */
373 char **argv
= malloc(sizeof(char *) * length
);
377 for(i
= 0; i
< length
; i
++) {
378 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
380 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
381 !NPVARIANT_IS_STRING(element
))
382 return false; /* TODO: clear up */
383 NPString string
= NPVARIANT_TO_STRING(element
);
384 char *copy
= malloc(string
.utf8length
+ 1);
386 return false; /* TODO: clear up */
387 /* TODO: allow sending any data, not just UTF-8 */
388 memcpy(copy
, string
.utf8characters
, string
.utf8length
);
389 copy
[string
.utf8length
] = 0;
391 NPN_ReleaseVariantValue(&element
);
394 *result_size
= length
;
398 static struct NaClDesc
*
399 LaunchProcess(NPP plugin
, const char *executable
, char **argv
, int argv_size
,
400 NPObject
*receive_callback
)
403 if(NaClSocketPair(socks
) < 0) {
404 perror("socketpair");
412 return NULL
; /* TODO: report error */
417 snprintf(fd_buffer
, sizeof(fd_buffer
), "%i:%i", 3, socks
[0]);
419 int args_len
= 7 + argv_size
;
420 const char *args
[args_len
];
422 args
[i
++] = "sel_ldr";
424 args
[i
++] = fd_buffer
;
426 args
[i
++] = executable
;
429 for(j
= 0; j
< argv_size
; j
++)
432 assert(i
== args_len
);
434 execvp("sel_ldr", (char **) args
);
439 if(fcntl(socks
[1], F_SETFD
, FD_CLOEXEC
) < 0) {
444 struct NaClDesc
*desc
= malloc(sizeof(struct NaClDescImcDesc
));
445 if(!NaClDescImcDescCtor((struct NaClDescImcDesc
*) desc
, socks
[1]))
448 struct ReceiveThreadArgs
*args
= malloc(sizeof(struct ReceiveThreadArgs
));
452 NPN_RetainObject(receive_callback
);
453 args
->plugin
= plugin
;
455 args
->callback
= receive_callback
;
458 pthread_attr_init(&attr
);
459 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
460 if(pthread_create(&thread_id
, &attr
, ReceiveThread
, args
) != 0)
467 LauncherObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
468 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
470 struct LauncherObj
*obj
= (struct LauncherObj
*) npobj
;
471 print(("Obj_Invoke %i\n", argCount
));
472 PrintIdentifier(methodName
);
473 if(methodName
== ident_launch
) {
475 NPVARIANT_IS_OBJECT(args
[0]) &&
476 NPVARIANT_IS_OBJECT(args
[1]) &&
477 NPVARIANT_IS_OBJECT(args
[2])) {
478 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(args
[0]);
479 NPObject
*argv_obj
= NPVARIANT_TO_OBJECT(args
[1]);
480 NPObject
*receive_callback
= NPVARIANT_TO_OBJECT(args
[2]);
483 if(IsType(file_npobj
, &FileObj_Vtable
) &&
484 ConvertArgvArray(obj
->plugin
, argv_obj
, &argv
, &argv_size
)) {
485 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
486 struct NaClDesc
*desc
= LaunchProcess(obj
->plugin
, file_obj
->filename
,
490 for(i
= 0; i
< argv_size
; i
++)
495 NPObject
*send_npobj
= NPN_CreateObject(obj
->plugin
, &SendObj_Vtable
);
496 struct SendObj
*send_obj
= (struct SendObj
*) send_npobj
;
497 send_obj
->desc
= desc
;
498 OBJECT_TO_NPVARIANT(send_npobj
, *result
);
503 VOID_TO_NPVARIANT(*result
);
506 if(methodName
== ident_get_file
) {
508 NPVARIANT_IS_STRING(args
[0]) &&
509 NPVARIANT_IS_OBJECT(args
[1])) {
510 /* TODO: do same-origin check (assuming they are useful). */
511 NPString url
= NPVARIANT_TO_STRING(args
[0]);
512 NPObject
*callback
= NPVARIANT_TO_OBJECT(args
[1]);
513 NPN_RetainObject(callback
);
514 /* TODO: I don't think utf8characters is guaranteed to be
516 NPError err
= NPN_GetURLNotify(obj
->plugin
, url
.utf8characters
,
518 print(("NPN_GetUrlNotify = %i\n", err
));
520 /* TODO: handle errors */
521 VOID_TO_NPVARIANT(*result
);
527 static NPClass LauncherObj_Vtable
= {
528 .structVersion
= NP_CLASS_STRUCT_VERSION
,
529 .allocate
= LauncherObj_Allocate
,
532 .hasMethod
= LauncherObj_HasMethod
,
533 .invoke
= LauncherObj_Invoke
,
534 .invokeDefault
= NULL
,
535 .hasProperty
= NoOp_HasProperty
,
536 .getProperty
= NoOp_GetProperty
,
538 .removeProperty
= NULL
,
543 NPP_New(NPMIMEType pluginType
, NPP instance
, uint16 mode
,
544 int16 argc
, char **argn
, char **argv
, NPSavedData
*saved
)
546 return NPERR_NO_ERROR
;
550 NPP_Destroy(NPP instance
, NPSavedData
**save
)
552 /* TODO: kill sel_ldr subprocesses, and do waitpid() to avoid a zombie */
553 print(("NPP_Destroy\n"));
554 return NPERR_NO_ERROR
;
558 NPP_GetValue(NPP instance
, NPPVariable variable
, void *result
)
561 case NPPVpluginScriptableNPObject
:
563 NPObject
*obj
= NPN_CreateObject(instance
, &LauncherObj_Vtable
);
564 *(NPObject
**) result
= obj
;
568 return NPERR_GENERIC_ERROR
;
570 return NPERR_NO_ERROR
;
574 NPP_NewStream(NPP instance
, NPMIMEType type
, NPStream
*stream
,
575 NPBool seekable
, uint16
*stype
)
577 print(("NPP_NewStream type=%s seekable=%i data=%p url=%s\n", type
, seekable
,
578 stream
->notifyData
, stream
->url
));
579 *stype
= NP_ASFILEONLY
;
580 return NPERR_NO_ERROR
;
584 NPP_StreamAsFile(NPP instance
, NPStream
*stream
, const char *filename
)
586 print(("NPP_StreamAsFile \"%s\"\n", filename
));
588 /* TODO: this function calls LOG_FATAL on error; not good */
589 struct NaClDesc
*desc
= (struct NaClDesc
*)
590 NaClDescIoDescOpen((char *) filename
, NACL_ABI_O_RDONLY
, 0);
594 NPObject
*callback
= stream
->notifyData
;
595 NPObject
*file_npobj
= NPN_CreateObject(instance
, &FileObj_Vtable
);
596 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
597 file_obj
->desc
= desc
;
598 file_obj
->filename
= strdup(filename
);
599 if(file_obj
->filename
== NULL
)
603 OBJECT_TO_NPVARIANT(file_npobj
, args
[0]);
604 bool success
= NPN_InvokeDefault(instance
, callback
, args
, 1, &result
);
605 print(("NPN_InvokeDefault %i\n", success
));
606 NPN_ReleaseVariantValue(&result
);
607 NPN_ReleaseObject(callback
);
608 NPN_ReleaseObject(file_npobj
);
612 NPP_URLNotify(NPP instance
, const char *url
, NPReason reason
, void *notifyData
)
614 /* TODO: handle file not found errors */
615 print(("NPP_URLNotify \"%s\"\n", url
));
620 NP_Initialize(NPNetscapeFuncs
*funcs
, NPPluginFuncs
*callbacks
)
622 print(("NP_Initialize\n"));
624 callbacks
->newp
= NPP_New
;
625 callbacks
->destroy
= NPP_Destroy
;
626 callbacks
->getvalue
= NPP_GetValue
;
627 callbacks
->newstream
= NPP_NewStream
;
628 callbacks
->asfile
= NPP_StreamAsFile
;
629 callbacks
->urlnotify
= NPP_URLNotify
;
631 ident_launch
= NPN_GetStringIdentifier("launch");
632 ident_get_file
= NPN_GetStringIdentifier("get_file");
633 ident_send
= NPN_GetStringIdentifier("send");
634 ident_length
= NPN_GetStringIdentifier("length");
636 NaClNrdAllModulesInit();
638 return NPERR_NO_ERROR
;
642 NP_GetMIMEDescription(void)
644 return "application/x-nacl-imc::Native Client IMC plugin";