I tried to achieve at first a swf reader with gtk2 on my unix machine. Worked, I could render simple swf files. Now, I'm trying to add a configuration to my flash file with xml configuration, add image, etc. Failed, won't get pass geturlnotify().
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "npupp.h"
#define FLASH_PLUGIN_SO "./libflashplayer.so"
void *flash_plugin_handle;
NPNetscapeFuncs browserFuncs;
NPPluginFuncs pluginFuncs;
GtkWidget *main_window;
char* fileName = NULL;
NPStream * stream;
const char * uagent = "Axt/1.0";
//Default window size
int WINDOW_XSIZE = 800;
int WINDOW_YSIZE = 600;
//Default child window position (flash player)
int xPosition = 0;
int yPosition = 0;
NPError (*iNP_Initialize)(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
NPError (*iNP_Shutdown)();
char* (*iNP_GetMIMEDescription)();
void* loadFlashPluginSo() {
void *handle;
handle = dlopen(FLASH_PLUGIN_SO, RTLD_LAZY | RTLD_LOCAL);
if(!handle) {
fprintf(stderr, "[-] error loading libflashplayer.so: %s\n", dlerror());
exit(1);
}
fprintf(stderr,"[+] loaded libflashplayer.so\n");
return handle;
}
void* loadSymbol(void *handle, const char *name) {
char *error;
void *ret;
ret = dlsym(handle, name);
if((error = dlerror()) != NULL) {
fprintf(stderr, "[-] error loading symbol %s: %s\n", name, error);
exit(1);
} else {
fprintf(stderr,"[+] loaded symbol %s, address: %p\n", name, ret);
}
return ret;
}
void loadNPEntryPoints(void *handle) {
iNP_Initialize=loadSymbol(handle, "NP_Initialize");
iNP_Shutdown=loadSymbol(handle, "NP_Shutdown");
iNP_GetMIMEDescription = loadSymbol(handle,"NP_GetMIMEDescription");
}
void printPluginEntrypoints(NPPluginFuncs* pFuncs) {
fprintf(stderr,"[*] NPP struct:\n");
fprintf(stderr,"\t- NPP_size: %8d\n",pFuncs->size);
fprintf(stderr,"\t- NPP_version: %8d\n",pFuncs->version);
fprintf(stderr,"\t- NPP_NewProcPtr: %p\n", pFuncs->newp);
fprintf(stderr,"\t- NPP_DestroyProcPtr: %p\n", pFuncs->destroy);
fprintf(stderr,"\t- NPP_SetWindowProcPtr: %p\n", pFuncs->setwindow);
fprintf(stderr,"\t- NPP_NewStreamProcPtr: %p\n", pFuncs->newstream);
fprintf(stderr,"\t- NPP_DestroyStreamProcPtr: %p\n", pFuncs->destroystream);
fprintf(stderr,"\t- NPP_StreamAsFileProcPtr: %p\n", pFuncs->asfile);
fprintf(stderr,"\t- NPP_WriteReadyProcPtr: %p\n", pFuncs->writeready);
fprintf(stderr,"\t- NPP_WriteProcPtr: %p\n", pFuncs->write);
fprintf(stderr,"\t- NPP_PrintProcPtr: %p\n", pFuncs->print);
fprintf(stderr,"\t- NPP_HandleEventProcPtr: %p\n", pFuncs->event);
fprintf(stderr,"\t- NPP_URLNotifyProcPtr: %p\n", pFuncs->urlnotify);
fprintf(stderr,"\t- javaClass: %p\n", pFuncs->javaClass);
fprintf(stderr,"\t- NPP_GetValueProcPtr: %p\n", pFuncs->getvalue);
fprintf(stderr,"\t- NPP_SetValueProcPtr: %p\n", pFuncs->setvalue);
}
NPError NPN_GetValueProc(NPP instance, NPNVariable variable, void *ret_value) {
fprintf(stderr,"[D] NPN_GetValueProc instance:%p, variable:%d, abi_mask:%d\n", instance, variable, 0);
switch (variable) {
case NPNVSupportsXEmbedBool:
*((int*)ret_value)= PR_TRUE;
break;
//Unix and solaris fix
case NPNVToolkit:
*((int*)ret_value)= NPNVGtk2;
break;
case NPNVnetscapeWindow:
*((int*)ret_value)= PR_TRUE;
break;
default:
*((int*)ret_value)=PR_FALSE;
break;
}
return NPERR_NO_ERROR;
}
const char* NPN_UserAgentProc(NPP instance) {
fprintf(stderr,"[D] NPN_UserAgentProc instance:%p\n", instance);
return uagent;
}
NPError NPN_GetURLProc(NPP instance, const char* url, const char* window) {
fprintf(stderr,"[D] NPN_GetURLProcPtr:%p, url: %s, window: %s\n", instance, url, window);
return NPERR_NO_ERROR;
}
NPIdentifier NPN_GetStringIdentifierProc(const NPUTF8* name) {
return (NPIdentifier)0x41424344; //Unique
}
static
gboolean plug_removed_cb (GtkWidget *widget, gpointer data) {
fprintf(stderr,"[!] plug_removed_cb\n");
return TRUE;
}
static void
socket_unrealize_cb(GtkWidget *widget, gpointer data) {
fprintf(stderr, "[!] socket_unrealize_cb\n");
}
static NPWindow *
npwindow_construct (GtkWidget *widget) {
NPWindow *npwindow;
NPSetWindowCallbackStruct *ws_info = NULL;
GdkWindow *parent_win = widget->window;
GtkWidget *socketWidget = gtk_socket_new();
gtk_widget_set_parent_window(socketWidget, parent_win);
gtk_widget_set_uposition(socketWidget, xPosition, yPosition);
g_signal_connect(socketWidget, "plug_removed", G_CALLBACK(plug_removed_cb), NULL);
g_signal_connect(socketWidget, "unrealize", G_CALLBACK(socket_unrealize_cb), NULL);
g_signal_connect(socketWidget, "destroy", G_CALLBACK(gtk_widget_destroyed), &socketWidget);
gpointer user_data = NULL;
gdk_window_get_user_data(parent_win, &user_data);
GtkContainer *container = GTK_CONTAINER(user_data);
gtk_container_add(container, socketWidget);
gtk_widget_realize(socketWidget);
GtkAllocation new_allocation;
new_allocation.x = 0;
new_allocation.y = 0;
new_allocation.width = WINDOW_XSIZE;
new_allocation.height = WINDOW_YSIZE;
gtk_widget_size_allocate(socketWidget, &new_allocation);
gtk_widget_show(socketWidget);
gdk_flush();
GdkNativeWindow ww = gtk_socket_get_id(GTK_SOCKET(socketWidget));
GdkWindow *w = gdk_window_lookup(ww);
npwindow = malloc (sizeof (NPWindow));
npwindow->window = (void*)(unsigned long)ww;
npwindow->x = 0;
npwindow->y = 0;
npwindow->width = WINDOW_XSIZE;
npwindow->height = WINDOW_YSIZE;
ws_info = malloc(sizeof (NPSetWindowCallbackStruct));
ws_info->type = NP_SETWINDOW;
ws_info->display = GDK_WINDOW_XDISPLAY(w);
ws_info->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(w));
GdkVisual* gdkVisual = gdk_drawable_get_visual(w);
ws_info->visual = GDK_VISUAL_XVISUAL(gdkVisual);
ws_info->depth = gdkVisual->depth;
npwindow->ws_info = ws_info;
npwindow->type = NPWindowTypeWindow;
return npwindow;
}
static NPStream *
npstream_construct() {
NPStream *stream = malloc(sizeof(NPStream));
stream->url=fileName;
stream->notifyData = 0x00000000;
fprintf(stderr,"[D] NPN_StreamConstructed: %p\n", stream);
return stream;
}
bool NPN_GetPropertyProc(NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result) {
fprintf(stderr,"[D] NPN_GetPropertyProc: %p\n", result);
result->type = NPVariantType_Object;
result->value.objectValue= (NPObject*)1;
return TRUE;
}
bool NPN_InvokeProc(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result) {
fprintf(stderr,"[D] NPN_InvokeProc: %p\n", result);
result->type= NPVariantType_String;
result->value.stringValue.utf8characters=fileName;
result->value.stringValue.utf8length=strlen(fileName);
return TRUE;
}
void NPN_ReleaseVariantValueProc(NPVariant *variant) {
}
void NPN_ReleaseObjectProc(NPObject *obj) {
}
NPObject* NPN_CreateObjectProc(NPP npp, NPClass *aClass) {
return (NPObject*)1;
}
NPObject* NPN_RetainObjectProc(NPObject *obj) {
return (NPObject*)1;
}
NPError NPN_GetURLNotifyProc(NPP instance, const char* url, const char* window, void* notifyData) {
fprintf(stderr,"[D] NPN_GetURLNotifyProc:%p, url: %s, window: %s\n", instance, url, window);
return 0;
}
NPN_GetURL, NPN_GetURLNotify, and NPP_URLNotify
void initNPNetscapeFuncs(NPNetscapeFuncs *bFuncs) {
int i=0;
for(i=1; i<sizeof(*bFuncs)/sizeof(ssize_t); i++)
*(((ssize_t*)bFuncs)+i)=i+1000;
bFuncs->geturl=NPN_GetURLProc;
bFuncs->getvalue=NPN_GetValueProc;
bFuncs->uagent=NPN_UserAgentProc;
bFuncs->getproperty=NPN_GetPropertyProc;
bFuncs->getstringidentifier=NPN_GetStringIdentifierProc;
bFuncs->invoke=NPN_InvokeProc;
bFuncs->releasevariantvalue=NPN_ReleaseVariantValueProc;
bFuncs->releaseobject=NPN_ReleaseObjectProc;
bFuncs->createobject=NPN_CreateObjectProc;
bFuncs->retainobject=NPN_RetainObjectProc;
bFuncs->geturlnotify=NPN_GetURLNotifyProc;
bFuncs->size= sizeof(bFuncs);
bFuncs->version= (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;;
}
static void destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit ();
}
static void checkError(const char* str, NPError err) {
if(err == NPERR_NO_ERROR)
fprintf(stderr, "[+] %s: success\n", str);
else
fprintf(stderr, "[-] %s: failed (%d)\n", str, err);
fflush(NULL);
}
int main(int argc, char **argv)
{
int c;
extern char *optarg;
while ((c = getopt(argc, argv, "f:w:h:x:y:")) != EOF){
switch (c) {
case 'f':
fileName = optarg;
fprintf (stderr, "[+] Filename: %s\n", optarg);
break;
case 'w':
WINDOW_XSIZE = atoi(optarg);
fprintf (stderr, "[+] WINDOW_XSIZE: %s\n", optarg);
break;
case 'h':
WINDOW_YSIZE = atoi(optarg);
fprintf (stderr, "[+] WINDOW_YSIZE: %s\n", optarg);
break;
case 'x':
xPosition = atoi(optarg);
fprintf (stderr, "[+] Position in x: %s\n", optarg);
break;
case 'y':
yPosition = atoi(optarg);
fprintf (stderr, "[+] Position in y: %s\n", optarg);
break;
case '?':
if (optopt == 'f' | optopt == 'w' | optopt == 'h')
fprintf (stderr, "[-] Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "[-] Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,"[-] Unknown option character `\\x%x'.\n", optopt);
exit(-1);
default:
fprintf(stderr,"[-] Usage: %s -f <swffile> -x <xsize> -y <ysize>\n", argv[0]);
exit(-1);
}
}
gtk_init (&argc, &argv);
main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize (main_window, WINDOW_XSIZE, WINDOW_YSIZE);
gtk_widget_set_uposition (main_window, xPosition, yPosition);
g_signal_connect (G_OBJECT (main_window), "destroy", G_CALLBACK (destroy), NULL);
gtk_widget_realize(main_window);
gtk_widget_show_all(main_window);
fprintf(stderr,"[+] created GTK widget\n");
flash_plugin_handle = loadFlashPluginSo();
loadNPEntryPoints(flash_plugin_handle);
fprintf(stderr,"[+] initialized flash plugin entry points\n");
initNPNetscapeFuncs(&browserFuncs);
fprintf(stderr,"[+] initialized browser functions\n");
checkError("NP_Initialize", iNP_Initialize(&browserFuncs, &pluginFuncs));
printPluginEntrypoints(&pluginFuncs);
NPWindow *npwin = npwindow_construct(main_window);
fprintf(stderr,"[+] created NPWindow widget\n");
NPP_t *instancep = malloc(sizeof(NPP_t));
memset(instancep,0,sizeof(sizeof(NPP_t)));
NPP instance = instancep;
NPSavedData* saved = malloc(sizeof(NPSavedData));
memset(saved,0,sizeof(sizeof(NPSavedData)));
stream = npstream_construct();
uint16_t stype;
char *xargv[]= {"quality", "bgcolor", "width", "height", "allowScriptAccess", "loop" };
char *xargm[]= {"high", "#000000", "1360", "768", "always", "false" };
checkError("NPN_New", pluginFuncs.newp("application/x-shockwave-flash", instance, NP_EMBED, 0, xargv, xargm, saved));
checkError("NPN_SetWindow", pluginFuncs.setwindow(instance, npwin));
checkError("NPN_NewStream", pluginFuncs.newstream(instance, "application/x-shockwave-flash", stream, 0, &stype));
FILE *pp;
char buffer[8192];
pp = fopen(fileName,"rb");
int len;
while((len=fread(buffer, 1, sizeof(buffer), pp)) != 0) {
pluginFuncs.writeready(instance, stream);
pluginFuncs.write(instance, stream, 0, len, buffer);
}
fclose(pp);
checkError("NPN_DestroyStream",pluginFuncs.destroystream(instance, stream, NPRES_DONE));
free(stream);
gtk_main ();
checkError("NPN_Destroy",pluginFuncs.destroy(instance, &saved));
checkError("NP_Shutdown", iNP_Shutdown());
dlclose(flash_plugin_handle);
return 0;
}
If I load a swf with no xml. It works.
When I had a config to my xml. It stops.
Here's the ouput I get:
~/test-flash$ ./test-flash -f xml_sample.swf -w 400 -h 600 -x 15 -y 15
[+] Filename: xml_sample.swf
[+] WINDOW_XSIZE: 400
[+] WINDOW_YSIZE: 600
[+] Position in x: 15
[+] Position in y: 15
[+] created GTK widget
[+] loaded libflashplayer.so
[+] loaded symbol NP_Initialize, address: 0x7ff03fb634d0
[+] loaded symbol NP_Shutdown, address: 0x7ff03fb634c0
[+] loaded symbol NP_GetMIMEDescription, address: 0x7ff03fb63870
[+] initialized flash plugin entry points
[+] initialized browser functions
[+] NP_Initialize: success
[*] NPP struct:
- NPP_size: 0
- NPP_version: 0
- NPP_NewProcPtr: 0x7ff03fb63990
- NPP_DestroyProcPtr: 0x7ff03fb63980
- NPP_SetWindowProcPtr: 0x7ff03fb63970
- NPP_NewStreamProcPtr: 0x7ff03fb63960
- NPP_DestroyStreamProcPtr: 0x7ff03fb63910
- NPP_StreamAsFileProcPtr: 0x7ff03fb63920
- NPP_WriteReadyProcPtr: 0x7ff03fb63950
- NPP_WriteProcPtr: 0x7ff03fb63940
- NPP_PrintProcPtr: 0x7ff03fb63900
- NPP_HandleEventProcPtr: 0x7ff03fb638f0
- NPP_URLNotifyProcPtr: 0x7ff03fb63930
- javaClass: (nil)
- NPP_GetValueProcPtr: 0x7ff03fb63860
- NPP_SetValueProcPtr: (nil)
[+] created NPWindow widget
[D] NPN_StreamConstructed: 0x25ce5e0
[D] NPN_GetValueProc instance:0x25cc720, variable:14, abi_mask:0
[D] NPN_GetValueProc instance:0x25cc720, variable:268435469, abi_mask:0
[D] NPN_UserAgentProc instance:(nil)
[D] NPN_GetValueProc instance:0x25cc720, variable:15, abi_mask:0
[D] NPN_GetValueProc instance:0x25cc720, variable:15, abi_mask:0
[D] NPN_GetValueProc instance:0x25cc720, variable:18, abi_mask:0
[+] NPN_New: success
[D] NPN_GetValueProc instance:0x25cc720, variable:14, abi_mask:0
[+] NPN_SetWindow: success
[D] NPN_GetURLNotifyProc:0x25cc720, url: javascript:top.location+"__flashplugin_unique__", window: (null)
[D] NPN_GetValueProc instance:0x25cc720, variable:15, abi_mask:0
[+] NPN_NewStream: success
Lenght: 455
[D] NPN_UserAgentProc instance:0x25cc720
[+] NPN_DestroyStream: success
[D] NPN_GetURLNotifyProc:0x25cc720, url: sample.xml, window: (null)
Thank you!
EDIT:
I tried something like this (code is in NPN_GetURLNotifyProc function) . but then, my app freeze inside my geturlnotifyproc...
NPStream s;
uint16 stype;
memset(&s,0,sizeof(NPStream));
s.url = strdup(url);
fprintf(stderr, "URL: %s\n", s.url);
checkError("NPN_NewStream", pluginFuncs.newstream(instance,"text/html",&s,0,&stype));
writeStream(instance, &pluginFuncs, &s);
pluginFuncs.urlnotify(instance,url,NPRES_DONE,notifyData);
checkError("NPN_DestroyStream", pluginFuncs.destroystream(instance,&s,NPRES_DONE));
free((void*)s.url);
I forgot to add the notify to my stream...
NPError NPN_GetURLNotifyProc(NPP instance, const char* url, const char* target, void* notifyData) {
fprintf(stderr,"[D] NPN_GetURLNotifyProc:%p, url: %s, window: %s, data: %p\n", instance, url, target, notifyData);
NPStream s;
uint16 stype;
memset(&s,0,sizeof(NPStream));
s.url = strdup(url);
s.notifyData = notifyData;
fprintf(stderr, "NPP: %p URL: %s\n", instance, url);
checkError("NPN_NewStream", pluginFuncs.newstream(instance,"text/html",&s,0,&stype));
writeStream(instance, &pluginFuncs, &s);
checkError("NPN_DestroyStream", pluginFuncs.destroystream(instance,&s,NPRES_DONE));
free((void*)s.url);
pluginFuncs.urlnotify(instance, url, NPRES_DONE, notifyData);
return 0;
}
I have now a standalone gtk application who can run compiled flash (swf).
Hope it helps someone in the future. I recommend doing a plugin to read another plugin (aka Sniffer) to actually know which function is called ("and how things works") to have a decent log file to work with.
Related
I am trying to write a simple application to record audio on my galaxy watch using native app development in C.
So far I got to the stage when I start the app and have a button that starts and stops the recording.
For some reason I cannot get the recorder to work, it returns rerror -38, which is Internal error, in the logs I can see this:
mm_camcorder_audiorec.c: _mmcamcorder_audio_command(530) > file delete(/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/AUDIO-2019-11-24_01:15:51.3gp)
But I have no idea why it's deleted on recorder_commit.
What am I missing? Can you help?
Code for the application:
#include <app.h>
#include <stddef.h>
#include <privacy_privilege_manager.h>
// Functions for creating and managing user interface elements. Here are all the widgets,
// such as: windows, buttons, etc.
#include <Elementary.h>
//Functions for using callback for hardware Back button and for
//using extended widgets for a circle screen
#include <efl_extension.h>
#include "audiorecorder.h"
#include <recorder.h>
#include <camera.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
typedef struct _UIData
{
Evas_Object *win;
Evas_Object *box;
Evas_Object *icon;
Evas_Object *button;
} UIData;
#define TEXT_BUF_SIZE 256
#define FILENAME_PREFIX "AUDIO"
#define NUMBER_OF_PRIVILEGES 3
#define START_RECORD_IMAGE_PATH "images/start-recording.jpg"
#define STOP_RECORD_IMAGE_PATH "images/stop-recording.png"
static recorder_h g_recorder;
bool permission_granted = false;
int user_clicked = 0;
static void
_state_changed_cb(recorder_state_e previous,
recorder_state_e current,
bool by_policy, void *user_data)
{
dlog_print(DLOG_INFO, LOG_TAG,
"_recorder_state_changed_cb (prev: %d, curr: %d)\n",
previous,
current);
}
static void
_recorder_recording_limit_reached_cb(
recorder_recording_limit_type_e type,
void *user_data)
{
dlog_print(DLOG_DEBUG, LOG_TAG, "Recording limit reached: %d\n", type);
}
/* Check the audio recorder state */
static bool
_recorder_expect_state(recorder_h recorder,
recorder_state_e expected_state)
{
recorder_state_e state;
int error_code = recorder_get_state(recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG,
"recorder state = %d, expected recorder state = %d, error_code = %d",
state, expected_state, error_code);
if (state == expected_state)
return true;
return false;
}
static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
UIData *ui = data;
elm_win_lower(ui->win);
}
static void
app_get_resource(const char *edj_file_in, char *edj_path_out, int edj_path_max)
{
char *res_path = app_get_resource_path();
if (res_path)
{
snprintf(edj_path_out, edj_path_max, "%s%s", res_path, edj_file_in);
free(res_path);
}
}
static void
_icon_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "icon create");
char image_path[PATH_MAX] =
{ 0, };
ui->icon = elm_image_add(ui->button);
elm_image_resizable_set(ui->icon, EINA_TRUE, EINA_TRUE);
app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
int error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "res path: %s error_code: %d", image_path, error_code);
evas_object_show(ui->icon);
}
static void
_button_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "button create");
ui->button = elm_button_add(ui->box);
_icon_create(ui);
elm_object_style_set(ui->button, "circle");
evas_object_size_hint_weight_set(ui->button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(ui->button, 0.5, 0.4);
evas_object_size_hint_min_set(ui->button, ELM_SCALE_SIZE(90), ELM_SCALE_SIZE(90));
elm_object_content_set(ui->button, ui->icon);
evas_object_show(ui->button);
}
static void
_box_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "box create");
ui->box = elm_box_add(ui->win);
evas_object_size_hint_weight_set(ui->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
_button_create(ui);
elm_object_content_set(ui->box, ui->button);
elm_box_pack_end(ui->box, ui->button);
evas_object_show(ui->box);
}
static void
_window_create(UIData *ui)
{
dlog_print(DLOG_INFO, LOG_TAG, "window create");
// Create and configure the main window
ui->win = elm_win_util_standard_add(NULL, NULL);
// Create a conformant - in this case, the main container in the application,
// which is also the interface between the application and
// system elements, such as a keyboard.
_box_create(ui);
// Set the size of the conformant widget the same as the size of the window
elm_win_resize_object_add(ui->win, ui->box);
//Register the function that will be called
//when you press the hardware Back button
eext_object_event_callback_add(ui->win, EEXT_CALLBACK_BACK, _win_back_cb, ui);
// /Always display the window only after the entire base
// user interface will be displayed.
evas_object_show(ui->win);
}
static void permission_request_cb(ppm_call_cause_e cause,
ppm_request_result_e result,
const char *privilege,
void *user_data)
{
dlog_print(DLOG_INFO, LOG_TAG, "callback called for privilege: %s with cause: %d, the result was: %d", privilege,
cause,
result);
user_clicked++;
}
void *func(void *arg)
{
while (user_clicked < NUMBER_OF_PRIVILEGES)
{
dlog_print(DLOG_INFO, LOG_TAG, "waiting for user to accept all privileges, so far accepted: %d", user_clicked);
sleep(1);
}
return 0;
}
static void _button_click_cb(void *data, Evas_Object *button, void *ev)
{
UIData *ui = data;
int error_code = 0;
dlog_print(DLOG_INFO, LOG_TAG, "clicked button!");
char image_path[PATH_MAX] = { 0, };
const char *image;
const char *group;
char *path = NULL;
recorder_state_e state;
elm_image_file_get(ui->icon, &image, &group);
dlog_print(DLOG_INFO, LOG_TAG, "file get, image: %s, group: %s", image, group);
if (strstr(image, "start") != NULL) {
app_get_resource(STOP_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting icon to stop, error code: %d", error_code);
dlog_print(DLOG_INFO, LOG_TAG, "starting recorder");
if (_recorder_expect_state(g_recorder, RECORDER_STATE_READY)
|| _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
{
error_code = recorder_start(g_recorder);
dlog_print(DLOG_INFO, LOG_TAG, "starting recorder result: %d", error_code);
}
else
{
recorder_get_state(g_recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
}
}
else if(strstr(image, "stop") != NULL)
{
app_get_resource(START_RECORD_IMAGE_PATH, image_path, (int) PATH_MAX);
error_code = elm_image_file_set(ui->icon, image_path, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting icon to start, error code: %d", error_code);
/* Stop the recorder and save the recorded data to a file */
if (_recorder_expect_state(g_recorder, RECORDER_STATE_RECORDING)
|| _recorder_expect_state(g_recorder, RECORDER_STATE_PAUSED))
{
error_code = recorder_get_filename(g_recorder, &path);
dlog_print(DLOG_INFO, LOG_TAG, "going to save audio to file %s error code: %d", path, error_code);
error_code = recorder_commit(g_recorder);
if (error_code != RECORDER_ERROR_NONE)
{
dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
recorder_cancel(g_recorder);
}
}
else
{
recorder_get_state(g_recorder, &state);
dlog_print(DLOG_INFO, LOG_TAG, "Failure, recorder in wrong state = %d", state);
}
}
}
static bool
_app_create_cb(void *data)
{
UIData *ui = data;
_window_create(ui);
evas_object_smart_callback_add(ui->button, "clicked", _button_click_cb, ui);
return true;
}
static void
_app_control_cb(app_control_h app_control, void *data)
{
/* Handle the launch request. */
}
static void
_app_pause_cb(void *data)
{
/* Take necessary actions when the application becomes invisible. */
}
static void
_app_resume_cb(void *data)
{
ppm_check_result_e privilege_results_array[NUMBER_OF_PRIVILEGES];
const char *privilege_array[NUMBER_OF_PRIVILEGES];
pthread_t pid;
char *recorder_privilege = "http://tizen.org/privilege/recorder";
char *media_storage_privilege = "http://tizen.org/privilege/mediastorage";
char *ex_media_storage_privilege = "http://tizen.org/privilege/externalstorage";
privilege_array[0] = malloc(strlen(recorder_privilege) + 1);
privilege_array[1] = malloc(strlen(media_storage_privilege) + 1);
privilege_array[2] = malloc(strlen(ex_media_storage_privilege) + 1);
strcpy((char*) privilege_array[0], recorder_privilege);
strcpy((char*) privilege_array[1], media_storage_privilege);
strcpy((char*) privilege_array[2], ex_media_storage_privilege);
int result = ppm_check_permissions(privilege_array, NUMBER_OF_PRIVILEGES, privilege_results_array);
dlog_print(DLOG_INFO, LOG_TAG, "checking permission for recorder. Result: %d", result);
for (int i = 0; i < NUMBER_OF_PRIVILEGES; i++)
{
switch (privilege_results_array[i])
{
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Allowed!", privilege_array[i]);
break;
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Denied!", privilege_array[i]);
break;
case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
dlog_print(DLOG_INFO, LOG_TAG, "Privilege: %s, Gotta ask for this privilege!", privilege_array[i]);
ppm_request_permission(privilege_array[i], permission_request_cb, NULL);
break;
}
}
pthread_create(&pid, NULL, func, NULL);
}
static void _app_terminate_cb(void *data)
{
UIData *ui = data;
//Before closing the application, delete the main widget (window),
// then all "children" widgets of this window (all widgets of this application) will be deleted.
evas_object_del(ui->win);
/* Release all resources. */
int error_code = recorder_unset_recording_limit_reached_cb(g_recorder);
error_code = recorder_unprepare(g_recorder);
error_code = recorder_unset_state_changed_cb(g_recorder);
error_code = recorder_destroy(g_recorder);
}
int main(int argc, char *argv[])
{
char filename[256] =
{ '\0' };
struct tm localtime =
{ 0 };
time_t rawtime = time(NULL);
int ret = 0;
size_t size = 0;
dlog_print(DLOG_INFO, LOG_TAG, "main");
UIData ui =
{ 0, };
ui_app_lifecycle_callback_s lifecycle_callbacks =
{ 0, };
/* Create the audio recorder handle */
int error_code = recorder_create_audiorecorder(&g_recorder);
if (error_code == RECORDER_ERROR_NONE)
dlog_print(DLOG_INFO, LOG_TAG, "successfully created audiorecorder error code = %d", error_code);
else
dlog_print(DLOG_ERROR, LOG_TAG, "Failure creating audiorecorder error code = %d", error_code);
error_code = recorder_set_state_changed_cb(g_recorder, _state_changed_cb, NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_state_changed_cb, error code = %d", error_code);
/* Set the audio encoder */
error_code = recorder_set_audio_encoder(g_recorder, RECORDER_AUDIO_CODEC_AAC);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_audio_encoder, error code = %d", error_code);
/* Set video encoder */
error_code = recorder_set_video_encoder(g_recorder, RECORDER_VIDEO_CODEC_MPEG4);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_video_encoder, error code = %d", error_code);
/* Set the maximum file size to unlimited 10240 (kB) - 10 mb */
error_code = recorder_attr_set_size_limit(g_recorder, 10240);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_size_limit, error code = %d", error_code);
/* Set the audio encoder bitrate */
error_code = recorder_attr_set_audio_encoder_bitrate(g_recorder, 28800);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_encoder_bitrate, error code = %d", error_code);
/* Set the audio device to microphone */
error_code = recorder_attr_set_audio_device(g_recorder, RECORDER_AUDIO_DEVICE_MIC);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_device, error code = %d", error_code);
/* Set the audio sample rate */
error_code = recorder_attr_set_audio_samplerate(g_recorder, 44100);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_attr_set_audio_samplerate, error code = %d", error_code);
/* Set the file format according to the audio encoder */
error_code = recorder_set_file_format(g_recorder, RECORDER_FILE_FORMAT_3GP);
dlog_print(DLOG_INFO, LOG_TAG, "setting recorder_set_file_format, error code = %d", error_code);
/* Create the file name */
dlog_print(DLOG_INFO, LOG_TAG, "data path = %s", app_get_data_path());
if (localtime_r(&rawtime, &localtime) != NULL)
{
size = snprintf(filename, sizeof(filename),
"%s%s-%04i-%02i-%02i_%02i:%02i:%02i.3gp",
app_get_data_path(),
FILENAME_PREFIX,
localtime.tm_year + 1900, localtime.tm_mon + 1,
localtime.tm_mday,
localtime.tm_hour, localtime.tm_min,
localtime.tm_sec);
}
else
{
dlog_print(DLOG_INFO, LOG_TAG, "failed to save audio in a file");
}
/* Set the full path and file name */
/* Set the file name according to the file format */
error_code = recorder_set_filename(g_recorder, filename);
dlog_print(DLOG_INFO, LOG_TAG, "setting file name to: %s, error code = %d",
filename,
error_code);
error_code = recorder_set_recording_limit_reached_cb(g_recorder,
_recorder_recording_limit_reached_cb,
NULL);
dlog_print(DLOG_INFO, LOG_TAG, "setting limit reached callback, error code = %d", error_code);
dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder");
error_code = recorder_prepare(g_recorder);
dlog_print(DLOG_INFO, LOG_TAG, "preparing recorder result: %d", error_code);
lifecycle_callbacks.create = _app_create_cb;
lifecycle_callbacks.terminate = _app_terminate_cb;
lifecycle_callbacks.pause = _app_pause_cb;
lifecycle_callbacks.resume = _app_resume_cb;
lifecycle_callbacks.app_control = _app_control_cb;
ret = ui_app_main(argc, argv, &lifecycle_callbacks, &ui);
if (ret != APP_ERROR_NONE)
{
dlog_print(DLOG_ERROR, LOG_TAG, "watch_app_main() is failed. err = %d",
ret);
}
return ret;
}
In your code, recorder_cancel is called if recorder_commit returns error.
error_code = recorder_commit(g_recorder);
if (error_code != RECORDER_ERROR_NONE)
{
dlog_print(DLOG_INFO, LOG_TAG, "Failure, error code = %d", error_code);
recorder_cancel(g_recorder);
}
So, we need to check why recorder_commit is failed.
Could you show all log since recorder_prepare is called especially with MM_CAMCORDER and GST_LOG log tags?
Note: You can get runtime error with recorder_set_error_cb(). It could help you to debug error.
Thank you for the log.
I have checked it and found that the recorder_commit() was failed because the recorded file size is zero.(I can assume it with time interval of recorder_commit() function call.)
According to log, the path for recording is "/opt/usr/home/owner/apps_rw/org.example.audiorecorder/data/" which is got by app_get_data_path().
But in general, media files are not placed there. So, I suggest to change the recording file path.
Please try it with another path which can be got by storage_foreach_device_supported().
(https://developer.tizen.org/development/api-references/native-application?redirect=https://developer.tizen.org/dev-guide/4.0.0/org.tizen.native.wearable.apireference/group__CAPI__SYSTEM__STORAGE__MODULE.html#ga8f5d871bbc2d8942fe6b725c8f8fc779)
And you can check recording status with recorder_set_recording_status_cb().
If it's not called repeatably, we can assume some problem is occurred.
Otherwise, we can assume it works well.
If recording status callback is not called, it means recorded file will not be created and there is a problem in audiosrc or encoding part of recorder framework.
Could you send me the your test tpk file?(jm80.yang#outlook.com)
I have tested my recording testsuite with same settings(samplerate:16000, bitrate:28800, audio codec:AAC, file format:3GP) on galaxy watch, but it works fine.
So I need to check the issue with test tpk which you made.
I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <getopt.h>
#include <stdbool.h>
#include <curl/curl.h>
#include <pthread.h>
#define BUFFER_SIZE 1024
#define SW_URL_STR_lEN 255
struct test_url {
char protocol[SW_URL_STR_lEN];
char hostname[SW_URL_STR_lEN];
char page[SW_URL_STR_lEN];
int port;
};
typedef struct test_url test_url_t;
test_url_t *test_create_url();
void test_destroy_url(test_url_t *url);
bool test_parse_url(test_url_t *url, const char *to_parse);
static int _getch(void) {
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
(void) ptr;
if (size * nmemb > BUFFER_SIZE) {
fprintf(stdout, "Error buffer too small");
exit(EXIT_FAILURE);
}
char *buf = (char *) userdata;
strncpy(buf, ptr, size * nmemb);
return size * nmemb;
}
static void send_rtsp_options_request(CURL *handle, const char *uri) {
fprintf(stdout, "\nRTSP: OPTIONS %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
curl_easy_perform(handle);
}
static void send_rtsp_describe_request(CURL *handle, const char *uri, char *str_sdp) {
fprintf(stdout, "\nRTSP: DESCRIBE %s\n", uri);
fprintf(stdout, " describe: %s", str_sdp);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, str_sdp);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
curl_easy_perform(handle);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, stdout);
}
static void send_rtsp_setup_request(CURL *handle, const char *uri, const char *transport) {
fprintf(stdout, "\nRTSP: SETUP %s\n", uri);
fprintf(stdout, " TRANSPORT %s\n", transport);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RTSP_TRANSPORT, transport);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
curl_easy_perform(handle);
}
static void send_rtsp_play_request(CURL *handle, const char *uri, const char *range) {
fprintf(stdout, "\nRTSP: PLAY %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RANGE, range);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
curl_easy_perform(handle);
/* Disable ranges */
curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
}
// static void send_rtsp_pause_request(CURL *handken, const char *uri, const char *range) {
// fprintf(stdout, "\nRTSP: PAUSE %s\n", uri);
// curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
// curl_easy_setopt(handle, CURLOPT_RANGE, range);
// curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PAUSE);
// curl_easy_perform(handle);
// /* Disable ranges */
// curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
// }
static void send_rtsp_teardown_request(CURL *handle, const char *uri) {
fprintf(stdout, "\nRTSP: TEARDOWN %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
curl_easy_perform(handle);
}
/* Main options */
typedef enum {
rtsp_injector_arg_help = 'h',
rtsp_injector_arg_addr = 'a',
rtsp_injector_arg_verbose = 'v',
} rtsp_injector_arg_t;
static void rtsp_print_help() {
fprintf(stdout, "Usage: \n");
fprintf(stdout, "\t-%c, --help\n", rtsp_injector_arg_help);
fprintf(stdout, "\t\tprints this help, and exits\n");
fprintf(stdout, "\n");
fprintf(stdout, "\t-%c, --rtsp-addr\n", rtsp_injector_arg_addr);
fprintf(stdout, "\t\tThe rtsp address of the server, with the port and the right camera choice such as \
\n\t\tActive camera - 'rtsp://(device ip address):5554/camera' \
\n\t\tBack camera - 'rtsp://(device ip address):5554/back' \
\n\t\tFront camera - 'rtsp://(device ip address):5554/front'\n");
fprintf(stdout, "\n");
fprintf(stdout, "\t-%c, --verbose\n", rtsp_injector_arg_verbose);
fprintf(stdout, "\t\tprints more details in the packets trading\n");
fprintf(stdout, "\n");
}
static void *pthread_proc(void *arg) {
(void)arg;
fprintf(stdout, "COIN COIN\n");
return NULL;
}
int main(int argc, char **argv) {
if (argc < 2) {
rtsp_print_help();
return EXIT_FAILURE;
}
static struct option long_options[] = {
{"help", no_argument, NULL, rtsp_injector_arg_help },
{"rtsp-addr", required_argument, NULL, rtsp_injector_arg_addr },
{"verbose", no_argument, NULL, rtsp_injector_arg_verbose },
{ NULL, 0, NULL, 0 },
};
int option_index = 0;
int c = 0;
char opstring[1024] = {0};
size_t s = 0;
pthread_t thrd;
// pthread_create(&thrd, NULL, pthread_proc, NULL);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_help);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c:", rtsp_injector_arg_addr);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_verbose);
long verbose_is_on = 0L;
char *url = NULL;
while ((c = getopt_long(argc, argv, opstring, long_options, &option_index)) != -1) {
switch (c) {
case rtsp_injector_arg_help:
rtsp_print_help();
return EXIT_SUCCESS;
case rtsp_injector_arg_addr:
url = optarg;
break;
case rtsp_injector_arg_verbose:
verbose_is_on = 1L;
break;
default:
fprintf(stdout, "Invalid option 0%o: '%c' ??\n", c, c);
rtsp_print_help();
return EXIT_FAILURE;
}
}
if (url == NULL) {
fprintf(stdout, "Missing mandatory prefix '-a' to the server address\n");
rtsp_print_help();
return EXIT_FAILURE;
}
int res = 0;
/* Where the PLAY starts and when should it ends */
const char *range = "0.000-";
char uri[1024] = {0};
// char control[1024] = {0};
char str_sdp[BUFFER_SIZE] = {0};
/* Initialize all libcurl sub moduless */
res = curl_global_init(CURL_GLOBAL_ALL);
if (res != CURLE_OK) {
fprintf(stdout, "curl_global_init failed\n");
return EXIT_FAILURE;
}
/* Create a sesion handle */
CURL *my_handle = curl_easy_init();
if (my_handle == NULL) {
fprintf(stdout, "curl_easy_init failed\n");
return EXIT_FAILURE;
}
/* Set the url to work with, must to be the right format ie RFC , the function doesn't check it */
curl_easy_setopt(my_handle, CURLOPT_URL, url);
/* Parse server information ie ip and port */
test_url_t *my_url = test_create_url();
test_parse_url(my_url, url);
fprintf(stdout, "Protocol: %s\nHostname: %s\nPort: %d\nPage: %s\n",
my_url->protocol, my_url->hostname, my_url->port, my_url->page);
curl_easy_setopt(my_handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(my_handle, CURLOPT_HEADERDATA, stdout);
curl_easy_setopt(my_handle, CURLOPT_USERAGENT, "test-client-rtsp");
curl_easy_setopt(my_handle, CURLOPT_VERBOSE, verbose_is_on);
/* Let's send some requests */
snprintf(uri, sizeof(uri), "%s", url);
/* OPTIONS */
send_rtsp_options_request(my_handle, uri);
/* DESCRIBE */
send_rtsp_describe_request(my_handle, uri, str_sdp);
fprintf(stdout, "Sdp information:\n%s", str_sdp);
// char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
char transport[1024] = "RTP/AVP;unicast;client_port=1234-1235";
fprintf(stdout, "Transport: %s", transport);
/* SETUP */
snprintf(uri, sizeof(uri), "%s", url);
strncat(uri, "/trackID=0", strlen(uri)); /* Different url for SETUP, check doc in trac with the scenari exemple */
send_rtsp_setup_request(my_handle, uri, transport);
/* PLAY */
snprintf(uri, sizeof(uri), "%s", url);
send_rtsp_play_request(my_handle, uri, range);
pthread_create(&thrd, NULL, pthread_proc, NULL);
_getch();
/* TEARDOWN */
send_rtsp_teardown_request(my_handle, uri);
/* Destroy the session handle */
curl_easy_cleanup(my_handle);
/* Cleanup curl init resources */
curl_global_cleanup();
test_destroy_url(my_url);
return EXIT_SUCCESS;
}
test_url_t * test_create_url() {
test_url_t *ret = NULL;
ret = malloc(sizeof(test_url_t));
if (!ret) {
return NULL;
}
memset(ret, 0, sizeof(test_url_t));
return ret;
}
void test_destroy_url(test_url_t *url) {
free(url);
}
bool test_parse_url(test_url_t *url, const char *to_parse) {
int pos_of_hostname = 0, pos_port = 0, pos_page = 0;
const char *hostname = NULL;
const char *str_port = NULL;
const char *str_page = NULL;
char str_port_number[4];
if (to_parse == NULL) {
return false;
}
hostname = strstr(to_parse, "://");
if (hostname == NULL) {
return false;
}
pos_of_hostname = hostname - to_parse; /* Get the position of "://" */
if (pos_of_hostname > (int)sizeof(url->protocol)) {
return false;
}
strncpy(url->protocol, to_parse, sizeof(url->protocol));
url->protocol[pos_of_hostname] = '\0';
hostname = to_parse + pos_of_hostname + 3; /* Skip protocol and '://' */
/* Check if port is provided */
str_port = strstr(hostname, ":");
if (str_port != NULL) {
pos_port = str_port - hostname;
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_port] = '\0';
str_page = strstr(hostname, "/");
if (str_page != NULL) { /* Check if page is provided */
pos_page = str_page - hostname;
str_page = hostname + pos_page + 1;
strncpy(url->page, str_page, sizeof(url->page));
if (pos_port < pos_page) { /* Check if ':' is before '/' ex: ':8080/something' */
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_port] = '\0';
/* Port number is provided */
str_page = hostname + pos_port + 1; /* Skip hostname and the ':' before port number' */
strncpy(str_port_number, str_page, sizeof(str_port_number));
url->port = atoi(str_port_number);
}
else { /* Page provided before port */
/* Warning: strncpy doesn't put a '\0' at the end of the string if src doesn't have one*/
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_page] = '\0';
}
}
else { /* There is no page provided */
str_page = hostname + pos_port + 1;
strncpy(str_port_number, str_page, sizeof(str_port_number));
url->port = atoi(str_port_number);
}
}
else { /* Only page provided */
str_page = strstr(hostname, "/");
if (str_page != NULL) { /* Check if page is provided */
pos_page = str_page - hostname;
str_page = hostname + pos_page + 1;
strncpy(url->page, str_page, sizeof(url->page));
url->page[strlen(str_page)] = '\0';
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_page] = '\0';
}
else { /* Case where there is only a hostname provided */
strncpy(url->hostname, hostname, sizeof(url->hostname));
}
}
return true;
}
The code crashes on pthread_create line 256 with the following backtrace:
Program received signal SIGSEGV, Segmentation fault.
__GI__IO_enable_locks () at genops.c:552
552 genops.c: No such file or directory.
(gdb) bt
#0 __GI__IO_enable_locks () at genops.c:552
#1 0x00007ffff7bbe52d in __pthread_create_2_1 (newthread=<optimized out>, attr=0x0, start_routine=<optimized out>, arg=0x0) at pthread_create.c:736
#2 0x0000555555557db5 in main (argc=3, argv=0x7fffffffd988) at test_client_rtsp.c:256
If I uncomment the first pthread_create, and comment the second one, it works fine. I take it this has to do with libcurl, but I'm not too sure it.
Curl 7.64.0 is compiled with:
./configure --disable-shared --enable-static --disable-ldap --disable-sspi --without-librtmp --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb --without-libidn --without-zlib --without-ssl --enable-rtsp
I'm kinda lost as to what is going on there.
I am trying to write some code, which can scan for nearby bluetooth devices.
I think it is a bit complicated to understand, so i am asking for help.
To start with I will explain my scenario. I know from the bluez adapter API (https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt), that i can call a method on dbus, which is taking no parameters to start scanning for devices. that method is called "StartDiscovery". I dont know if it would be a proper way to this asynchronously? And im also unsure about the function
g_main_loop_run()
I need to use it for the method to keep scanning for devices, but i dont know what the proper way is to stop the loop again.
Here is my code, assume that a GDBusProxy is obtained
start_discover_variant = g_dbus_proxy_call_sync(proxy,"StartDiscovery", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (start_discover_variant == NULL)
{
syslog(LOG_ERR, "%s", error->message);
g_error_free(error);
g_assert_no_error(error);
}
sleep(20);
stop_discover_variant = g_dbus_proxy_call_sync(proxy,"StopDiscovery", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (stop_discover_variant == NULL)
{
syslog(LOG_ERR, "%s", error->message);
g_error_free(error);
g_assert_no_error(error);
}
g_variant_unref(stop_discover_variant);
As you can see i implemented a 20 second sleep, but since it can run asynchronously, maybe some kind of timeout signal can be used instead, or is the sleep function ok?
From your sample code I understand that you stopping the scanning after 20 seconds, which is not asynchronous. Asynchronous means you need to take decision based on external or internal events. This can be POSIX signal/timer (using timer_create) or any events.
If you are sure about using timed/timeout based stop, better use "timer_create" in Linux or any other timer based operations. If you want to stop scanning based on scanned devices, then you can use callback for "StartDiscovery" results.
Here is the complete example of listing the devices after scanning using GDBUS (but not proxy). The same example can be extended using proxy.
/*
* bluez_adapter_scan.c - Scan for bluetooth devices
* - This example scans for new devices after powering the adapter, if any devices
* appeared in /org/hciX/dev_XX_YY_ZZ_AA_BB_CC, it is monitered using "InterfaceAdded"
* signal and all the properties of the device is printed
* - Scanning continues to run until any device is disappered, this happens after 180 seconds
* automatically if the device is not used.
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_scan ./bluez_adapter_scan.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(g_strcmp0(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_call_method(const char *method)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
int main(void)
{
GMainLoop *loop;
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_adapter_call_method("StartDiscovery");
if(rc) {
g_print("Not able to scan for new devices\n");
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_call_method("StopDiscovery");
if(rc)
g_print("Not able to stop scanning\n");
g_usleep(100);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
You can find more information about this example here. You can use "bluez_device_appeared" to decide to stop discovery or timer based if no devices appeared for X seconds.
You function can only call "StartDiscovery" and arm the timer using "timer_settime" by having callback in "struct sigevent sev.sigev_notify_function = stop_discovery_cb_handler;"
With this you can avoid sleeping in actual function and leave the callback to stop discovery.
I am trying to write an application which searches Bluetooth devices nearby and communicates with them. My application is going to be written in C, and intended to work under Linux.
Are there any tutorial or sample for working with BlueZ via D-Bus in C ?
Purpose of this application is to send data from a file in BLE.
Can you help me please?
You can find the nearby devices using "StartDiscovery" adapter API using DBUS. With the below sample code, you should be able to scan the nearby devices (both BLE and Calssic). But if you want to scan only the BLE devices nearby, you can use "SetDiscoveryFilter" API to set the transport to "le" and start scanning for the BLE devices (check the second example below).
/*
* bluez_adapter_scan.c - Scan for bluetooth devices
* - This example scans for new devices after powering the adapter, if any devices
* appeared in /org/hciX/dev_XX_YY_ZZ_AA_BB_CC, it is monitered using "InterfaceAdded"
* signal and all the properties of the device is printed
* - Scanning continues to run until any device is disappered, this happens after 180 seconds
* automatically if the device is not used.
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_scan ./bluez_adapter_scan.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(g_strcmp0(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_call_method(const char *method)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
int main(void)
{
GMainLoop *loop;
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_adapter_call_method("StartDiscovery");
if(rc) {
g_print("Not able to scan for new devices\n");
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_call_method("StopDiscovery");
if(rc)
g_print("Not able to stop scanning\n");
g_usleep(100);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
Example to use SetDiscoveryFilter to scan only for BLE devices.
/*
* bluez_adapter_filter.c - Set discovery filter, Scan for bluetooth devices
* - Control three discovery filter parameter from command line,
* - auto/bredr/le
* - RSSI (0:very close range to -100:far away)
* - UUID (only one as of now)
* Example run: ./bin/bluez_adapter_filter bredr 100 00001105-0000-1000-8000-00805f9b34fb
* - This example scans for new devices after powering the adapter, if any devices
* appeared in /org/hciX/dev_XX_YY_ZZ_AA_BB_CC, it is monitered using "InterfaceAdded"
* signal and all the properties of the device is printed
* - Device will be removed immediately after it appears in InterfacesAdded signal, so
* InterfacesRemoved will be called which quits the main loop
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_filter ./bluez_adapter_filter.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
typedef void (*method_cb_t)(GObject *, GAsyncResult *, gpointer);
static int bluez_adapter_call_method(const char *method, GVariant *param, method_cb_t method_cb)
{
GError *error = NULL;
g_dbus_connection_call(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
method_cb,
&error);
if(error != NULL)
return 1;
return 0;
}
static void bluez_get_discovery_filter_cb(GObject *con,
GAsyncResult *res,
gpointer data)
{
(void)data;
GVariant *result = NULL;
result = g_dbus_connection_call_finish((GDBusConnection *)con, res, NULL);
if(result == NULL)
g_print("Unable to get result for GetDiscoveryFilter\n");
if(result) {
result = g_variant_get_child_value(result, 0);
bluez_property_value("GetDiscoveryFilter", result);
}
g_variant_unref(result);
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
//int rc;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
/*
rc = bluez_adapter_call_method("RemoveDevice", g_variant_new("(o)", object));
if(rc)
g_print("Not able to remove %s\n", object);
*/
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(strcmp(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_set_discovery_filter(char **argv)
{
int rc;
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(b, "{sv}", "Transport", g_variant_new_string(argv[1]));
g_variant_builder_add(b, "{sv}", "RSSI", g_variant_new_int16(-g_ascii_strtod(argv[2], NULL)));
g_variant_builder_add(b, "{sv}", "DuplicateData", g_variant_new_boolean(FALSE));
GVariantBuilder *u = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY);
g_variant_builder_add(u, "s", argv[3]);
g_variant_builder_add(b, "{sv}", "UUIDs", g_variant_builder_end(u));
GVariant *device_dict = g_variant_builder_end(b);
g_variant_builder_unref(u);
g_variant_builder_unref(b);
rc = bluez_adapter_call_method("SetDiscoveryFilter", g_variant_new_tuple(&device_dict, 1), NULL);
if(rc) {
g_print("Not able to set discovery filter\n");
return 1;
}
rc = bluez_adapter_call_method("GetDiscoveryFilters",
NULL,
bluez_get_discovery_filter_cb);
if(rc) {
g_print("Not able to get discovery filter\n");
return 1;
}
return 0;
}
int main(int argc, char **argv)
{
GMainLoop *loop;
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
if(argc > 3) {
rc = bluez_set_discovery_filter(argv);
if(rc)
goto fail;
}
rc = bluez_adapter_call_method("StartDiscovery", NULL, NULL);
if(rc) {
g_print("Not able to scan for new devices\n");
goto fail;
}
g_main_loop_run(loop);
if(argc > 3) {
rc = bluez_adapter_call_method("SetDiscoveryFilter", NULL, NULL);
if(rc)
g_print("Not able to remove discovery filter\n");
}
rc = bluez_adapter_call_method("StopDiscovery", NULL, NULL);
if(rc)
g_print("Not able to stop scanning\n");
g_usleep(100);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
You can also find some explanation about StartDiscovery example here and SetDiscoveryFilter here.
I'm embedding a Flash SWF file in a Linux application using the NPAPI. Everything works fine but when the Flash calls "fscommand", I'm getting a security sandbox violation. I tried the program with several Linux distributions and several versions of the Flash Player (9,10,11) but no luck.
Does anybody know if this is a limitation of the Flash Player in Linux platforms?
In this post Closing gtk application when SWF ends (NPAPI) it seems they have been able to receive Javascript events.
This is the code I'm using:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
//#include "npupp.h"
#include "npapi.h"
#include "npfunctions.h"
//#ifdef __i386__
#define FLASH_PLUGIN_SO "./flash/libflashplayer32.so"
//#else
//#define FLASH_PLUGIN_SO "./flash/libflashplayer64.so"
//#endif
#define PR_TRUE 1
#define PR_FALSE 0
#define WINDOW_XSIZE 1600/2
#define WINDOW_YSIZE 900/2
#define NO_IDENTIFIER ((NPIdentifier)0)
#define SPECIAL_IDENTIFIER 0x0FEEBBCC
#define SPECIAL_METHOD_NAME "swhxCall"
#define FLASH_REQUEST "__flash__request"
#define FSCMD "_DoFSCommand"
#define INVOKE_RESPONSE "<invoke name=\"%s\" returntype=\"javascript\"><arguments><null/></arguments></invoke>"
typedef intptr_t int_val;
void *flash_plugin_handle;
NPNetscapeFuncs browserFuncs;
NPPluginFuncs pluginFuncs;
GtkWidget *main_window;
char* URL;
NPStream * stream;
const char * uagent = "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13";
NPError (*iNP_Initialize)(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
NPError (*iNP_Shutdown)();
char* (*iNP_GetMIMEDescription)();
// --- Property IDs
static char **np_ids = NULL;
static int_val np_id_count = 0;
static NPIdentifier resolveNPId( const char *id ) {
int_val i;
for(i=0;i<np_id_count;i++)
if( strcmp(np_ids[i],id) == 0 )
return (NPIdentifier)(i+1);
if( strcmp(id,SPECIAL_METHOD_NAME) == 0 )
return (NPIdentifier)SPECIAL_IDENTIFIER;
return NO_IDENTIFIER;
}
static NPIdentifier addNPId( const char *id ) {
NPIdentifier newid = resolveNPId(id);
if( newid == NO_IDENTIFIER ) {
np_id_count++;
printf("New npid added: %i == %s\n",np_id_count, id);
np_ids = realloc(np_ids,np_id_count*sizeof(char*));
np_ids[np_id_count-1] = strdup(id);
return (NPIdentifier)np_id_count;
}
return newid;
}
static const char *getNPId( NPIdentifier id ) {
int_val index = ((int_val)id)-1;
if( index >= 0 && index < np_id_count )
return np_ids[index];
if( id == (NPIdentifier)SPECIAL_IDENTIFIER )
return SPECIAL_METHOD_NAME;
return NULL;
}
static int matchNPId(NPIdentifier id, const char *str) {
const char *strid = getNPId(id);
return ( strid != NULL && strcmp(strid,str) == 0 );
}
void freeNPIds() {
while( np_id_count )
free(np_ids[--np_id_count]);
free(np_ids);
}
static bool NPN_InvokeProc( NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result ) ;
static bool NPN_InvokeDefaultProc( NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result ) ;
static bool NPN_GetPropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result ) ;
static bool NPN_SetPropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value ) ;
static bool NPN_RemovePropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName ) ;
static bool NPN_HasPropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName ) ;
static bool NPN_HasMethodProc( NPP npp, NPObject *npobj, NPIdentifier methodName ) ;
static NPObject *NPN_CreateObjectProc( NPP npp, NPClass *aClass );
static NPObject *NPN_RetainObjectProc( NPObject *npobj );
// Window class;
static NPClass __gen_class =
{ NP_CLASS_STRUCT_VERSION
, (NPAllocateFunctionPtr) malloc
, (NPDeallocateFunctionPtr) free
, 0
, (NPHasMethodFunctionPtr) NPN_HasMethodProc
, (NPInvokeFunctionPtr) NPN_InvokeProc
, (NPInvokeDefaultFunctionPtr)NPN_InvokeDefaultProc
, (NPHasPropertyFunctionPtr) NPN_HasPropertyProc
, (NPGetPropertyFunctionPtr) NPN_GetPropertyProc
, (NPSetPropertyFunctionPtr) NPN_SetPropertyProc
, (NPRemovePropertyFunctionPtr) NPN_RemovePropertyProc
};
static NPObject __window = { &__gen_class, 1 };
static NPObject __location = { &__gen_class, 1};
static NPObject __top = { &__gen_class, 1 };
static NPObject __top_location = { &__gen_class, 1 };
static void traceObjectOnCall(const char *f, NPObject *o){
if (o == &__top) printf("DOM object 'top': %s\n",f);
else if (o == &__window) printf("DOM object 'window': %s\n",f);
else if (o == &__location) printf("DOM object 'location': %s\n",f);
else if (o == &__top_location) printf("DOM object 'top.location': %s\n",f);
}
static void checkError(const char* str, NPError err) {
if(err == NPERR_NO_ERROR)
printf("[+] %s: success\n", str);
else
printf("[-] %s: failed (%d)\n", str, err);
fflush (stdout);
}
void* loadFlashPluginSo() {
void *handle;
handle = dlopen(FLASH_PLUGIN_SO, RTLD_LAZY | RTLD_LOCAL);
if(!handle) {
fprintf(stderr, "[-] error loading libflasplayer.so: %s\n", dlerror());
exit(1);
}
puts("[+] loaded libflashplayer.so");
return handle;
}
void* loadSymbol(void *handle, const char *name) {
char *error;
void *ret;
ret = dlsym(handle, name);
if((error = dlerror()) != NULL) {
fprintf(stderr, "[-] error loading symbol %s: %s\n", name, error);
exit(1);
} else {
printf("[+] loaded symbol %s, address: %p\n", name, ret);
}
return ret;
}
void loadNPEntryPoints(void *handle) {
iNP_Initialize=(NPError (*)(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs))loadSymbol(handle, "NP_Initialize");
iNP_Shutdown=(NPError (*)())loadSymbol(handle, "NP_Shutdown");
iNP_GetMIMEDescription = (char*(*)())loadSymbol(handle,"NP_GetMIMEDescription");
}
void printPluginEntrypoints(NPPluginFuncs* pFuncs) {
printf("[*] NPP struct:\n");
printf("\t- NPP_size: %8d\n",pFuncs->size);
printf("\t- NPP_version: %8d\n",pFuncs->version);
printf("\t- NPP_NewProcPtr: %p\n", pFuncs->newp);
printf("\t- NPP_DestroyProcPtr: %p\n", pFuncs->destroy);
printf("\t- NPP_SetWindowProcPtr: %p\n", pFuncs->setwindow);
printf("\t- NPP_NewStreamProcPtr: %p\n", pFuncs->newstream);
printf("\t- NPP_DestroyStreamProcPtr: %p\n", pFuncs->destroystream);
printf("\t- NPP_StreamAsFileProcPtr: %p\n", pFuncs->asfile);
printf("\t- NPP_WriteReadyProcPtr: %p\n", pFuncs->writeready);
printf("\t- NPP_WriteProcPtr: %p\n", pFuncs->write);
printf("\t- NPP_PrintProcPtr: %p\n", pFuncs->print);
printf("\t- NPP_HandleEventProcPtr: %p\n", pFuncs->event);
printf("\t- NPP_URLNotifyProcPtr: %p\n", pFuncs->urlnotify);
printf("\t- javaClass: %p\n", pFuncs->javaClass);
printf("\t- NPP_GetValueProcPtr: %p\n", pFuncs->getvalue);
printf("\t- NPP_SetValueProcPtr: %p\n", pFuncs->setvalue);
}
NPError NPN_SetValueProc(NPP instance, NPPVariable variable, void *value)
{
switch(variable) {
case NPPVpluginWindowBool:
printf( "NPPVpluginWindowBool - %p\n", value);
break;
default:
printf( "SetValue %i\n", variable );
break;
}
return NPERR_NO_ERROR;
}
NPError NPN_GetValueProc(NPP instance, NPNVariable variable, void *ret_value) {
printf("[D] NPN_GetValueProc instance:%p, variable:%d, abi_mask:%d\n", instance, variable, 0);
switch (variable) {
case NPNVSupportsXEmbedBool:
*((int*)ret_value)= PR_TRUE;
break;
case NPNVToolkit:
*((int*)ret_value)= NPNVGtk2;
break;
case NPNVnetscapeWindow:
*((int*)ret_value)= PR_TRUE;
break;
default:
*((int*)ret_value)=PR_FALSE;
break;
}
return NPERR_NO_ERROR;
}
const char* NPN_UserAgentProc(NPP instance) {
return uagent;
}
static void Status_( NPP instance, const char* message ) {
printf( "Status\n" );
}
uint32_t MemFlush( uint32_t size ) {
return 0;
}
void ReloadPlugins( NPBool reloadPages ) {
}
void * GetJavaEnv(void) {
return NULL;
}
void * GetJavaPeer( NPP instance ) {
return NULL;
}
NPIdentifier NPN_GetStringIdentifierProc(const NPUTF8* name) {
return addNPId(name);
}
void GetStringIdentifiers( const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers ) {
}
NPIdentifier GetIntIdentifier( int32_t intid ) {
return NO_IDENTIFIER;
}
bool IdentifierIsString( NPIdentifier id ) {
return getNPId(id) != NULL;
}
NPUTF8* UTF8FromIdentifier( NPIdentifier identifier ) {
const char *result = getNPId(identifier);
return result ? strdup(result) : NULL;
}
int32_t IntFromIdentifier( NPIdentifier id ) {
return 0;
}
static gboolean plug_removed_cb (GtkWidget *widget, gpointer data) {
printf("[!] plug_removed_cb\n");
return TRUE;
}
static void socket_unrealize_cb(GtkWidget *widget, gpointer data) {
printf("[!] socket_unrealize_cb\n");
}
static NPWindow * npwindow_construct (GtkWidget *widget) {
NPWindow *npwindow;
NPSetWindowCallbackStruct *ws_info = NULL;
GdkWindow *parent_win = widget->window;
GtkWidget *socketWidget = gtk_socket_new();
gtk_widget_set_parent_window(socketWidget, parent_win);
g_signal_connect(socketWidget, "plug_removed", G_CALLBACK(plug_removed_cb), NULL);
g_signal_connect(socketWidget, "unrealize", G_CALLBACK(socket_unrealize_cb), NULL);
g_signal_connect(socketWidget, "destroy", G_CALLBACK(gtk_widget_destroyed), &socketWidget);
gpointer user_data = NULL;
gdk_window_get_user_data(parent_win, &user_data);
GtkContainer *container = GTK_CONTAINER(user_data);
gtk_container_add(container, socketWidget);
gtk_widget_realize(socketWidget);
GtkAllocation new_allocation;
new_allocation.x = 0;
new_allocation.y = 0;
new_allocation.width = WINDOW_XSIZE;
new_allocation.height = WINDOW_YSIZE;
gtk_widget_size_allocate(socketWidget, &new_allocation);
gtk_widget_show(socketWidget);
gdk_flush();
GdkNativeWindow ww = gtk_socket_get_id(GTK_SOCKET(socketWidget));
GdkWindow *w = gdk_window_lookup(ww);
npwindow = (NPWindow*) malloc (sizeof (NPWindow));
npwindow->window = (void*)(unsigned long)ww;
npwindow->x = 0;
npwindow->y = 0;
npwindow->width = WINDOW_XSIZE;
npwindow->height = WINDOW_YSIZE;
ws_info = (NPSetWindowCallbackStruct*) malloc(sizeof (NPSetWindowCallbackStruct));
ws_info->type = NP_SETWINDOW;
ws_info->display = GDK_WINDOW_XDISPLAY(w);
ws_info->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(w));
GdkVisual* gdkVisual = gdk_drawable_get_visual(w);
ws_info->visual = GDK_VISUAL_XVISUAL(gdkVisual);
ws_info->depth = gdkVisual->depth;
npwindow->ws_info = ws_info;
npwindow->type = NPWindowTypeWindow;
return npwindow;
}
static NPStream * npstream_construct() {
NPStream *stream = (NPStream *) malloc(sizeof(NPStream));
stream->url=strdup(URL);
stream->ndata = 0;
stream->end = 99782;
stream->lastmodified= 1201822722;
stream->notifyData = 0x00000000;
stream->headers = (char*)malloc(200);
return stream;
}
bool NPN_InvokeProc( NPP npp, NPObject *npobj, NPIdentifier npid, const NPVariant *args, uint32_t argCount, NPVariant *result ) {
traceObjectOnCall(__FUNCTION__,npobj);
if( matchNPId(npid,FLASH_REQUEST) && argCount == 1 && args[0].type == NPVariantType_String ) {
return 1;
}
if( matchNPId(npid,FLASH_REQUEST) && argCount == 3 &&
args[0].type == NPVariantType_String &&
args[1].type == NPVariantType_String &&
args[2].type == NPVariantType_String
) {
return 1;
}
if( matchNPId(npid,"_DoFSCommand") && argCount == 2 && args[0].type == NPVariantType_String && args[1].type == NPVariantType_String ) {
printf("[D] FSCOMMAND\n");
return 1;
}
if( npobj == &__top_location ) {
if( matchNPId(npid,"toString") ) {
result->type = NPVariantType_String;
// "chrome://global/content/console.xul" is what Firefox returns for 'top.location.toString()';
result->value.stringValue.UTF8Characters = strdup("chrome://global/content/console.xul");
result->value.stringValue.UTF8Length = (int)strlen(result->value.stringValue.UTF8Characters);
printf("[D] Returned %s\n", result->value.stringValue.UTF8Characters);
}
return 1;
}
//On OSX, Flash retreives locations by injected functions:
if( matchNPId(npid,"__flash_getWindowLocation") ) {
// return the location object:
result->type = NPVariantType_Object;
result->value.objectValue = &__location;
NPN_RetainObjectProc(&__location);
return 1;
}
if( matchNPId(npid,"__flash_getTopLocation") ) {
// return the top_location object:
result->type = NPVariantType_Object;
result->value.objectValue = &__top_location;
NPN_RetainObjectProc(&__top_location);
return 1;
}
return 0;
}
static bool NPN_InvokeDefaultProc( NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result ) {
return 0;
}
static bool NPN_GetPropertyProc( NPP npp, NPObject *npobj, NPIdentifier npid, NPVariant *result ) {
if (npobj == &__window) {
if( matchNPId(npid,"location") ) {
result->type = NPVariantType_Object;
result->value.objectValue = &__location;
NPN_RetainObjectProc(&__location);
return 1;
}
if( matchNPId(npid,"top") ) {
result->type = NPVariantType_Object;
result->value.objectValue = &__top;
NPN_RetainObjectProc(&__top);
return 1;
}
} else if (npobj == &__top) {
if ( matchNPId(npid,"location") ) {
result->type = NPVariantType_Object;
result->value.objectValue = &__top_location;
NPN_RetainObjectProc(&__top_location);
return 1;
}
}
return 0;
}
static bool NPN_SetPropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value ) {
return 0;
}
static bool NPN_RemovePropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName ) {
return 0;
}
static bool NPN_HasPropertyProc( NPP npp, NPObject *npobj, NPIdentifier propertyName ) {
return 0;
}
static bool NPN_HasMethodProc( NPP npp, NPObject *npobj, NPIdentifier methodName ) {
return 0;
}
static int unescape( char *str, int *ssize ) {
int k = 0, esc = 0;
// UNESCAPE the string
while( k < *ssize ) {
if( !esc ) {
if( str[k++] == '\\' )
esc = 1;
} else {
char c;
switch( str[k] ) {
case '"': c = '"'; break;
case '\\': c = '\\'; break;
case 'r': c = '\r'; break;
case 'n': c = '\n'; break;
case 't': c = '\t'; break;
default:
return 0;
}
(*ssize)--;
memcpy(str+k,str+k+1,*ssize-k);
str[k-1] = c;
esc = 0;
}
}
str[*ssize] = 0;
return 1;
}
static const char *end_of_string( const char *send ) {
int esc = 0;
while( *send ) {
switch( *send ) {
case '"':
if( !esc )
return send;
esc = 0;
break;
case '\\':
esc = !esc;
break;
default:
esc = 0;
break;
}
send++;
}
return NULL;
}
#define JS_CALL_START "try { __flash__toXML("
#define JS_RESULT_START "var __flash_temp = \""
bool Evaluate( NPP npp, NPObject *npobj, NPString *script, NPVariant *result ) {
printf( "Evaluate %s\n", script->UTF8Characters );
if( memcmp(script->UTF8Characters,JS_CALL_START,strlen(JS_CALL_START)) == 0 ) {
const char *p = script->UTF8Characters + strlen(JS_CALL_START);
const char *s = p;
const char *send;
while( *s && *s != '(' )
s++;
if( !*s || s[1] != '"' )
return 0;
s += 2;
send = end_of_string(s);
if( send == NULL || send[1] != ')' )
return 0;
{
int isize = (int)(s - p) - 2;
int ssize = (int)(send - s);
char *ident = (char*)malloc(isize+1);
char *str = (char*)malloc(ssize+1);
memcpy(ident,p,isize);
memcpy(str,s,ssize);
ident[isize] = 0;
if( !unescape(str,&ssize) ) {
free(ident);
free(str);
return 0;
}
// CALLBACK
{
printf("CALLBACK\n");
int size = 0;
const char *res = "callback";
free(ident);
free(str);
if( res == NULL )
return 0;
result->type = NPVariantType_String;
result->value.stringValue.UTF8Characters = strdup(res);
result->value.stringValue.UTF8Length = size;
return 1;
}
}
}
if( memcmp(script->UTF8Characters,JS_RESULT_START,strlen(JS_RESULT_START)) == 0 ) {
const char *s = script->UTF8Characters + strlen(JS_RESULT_START);
const char *send = end_of_string(s);
char *str;
int ssize;
if( send == NULL || send[1] != ';' )
return 0;
ssize = (int)(send - s);
str = (char*)malloc(ssize+1);
memcpy(str,s,ssize);
if( !unescape(str,&ssize) ) {
free(str);
return 0;
}
result->type = NPVariantType_String;
result->value.stringValue.UTF8Characters = str;
result->value.stringValue.UTF8Length = ssize;
return 1;
}
result->type = NPVariantType_Void;
return 1;
}
void NPN_ReleaseObjectProc(NPObject *npobj) {
if( npobj == NULL )
return;
npobj->referenceCount--;
if( npobj->referenceCount != 0 )
return;
if( npobj->_class->deallocate ) {
npobj->_class->deallocate(npobj);
return;
}
if( npobj->_class->invalidate )
npobj->_class->invalidate(npobj);
free(npobj);
}
NPObject* NPN_CreateObjectProc(NPP npp, NPClass *aClass) {
NPObject *o;
if( aClass->allocate )
o = aClass->allocate(npp,aClass);
else
o = (NPObject*)malloc(sizeof(NPObject));
o->_class = aClass;
o->referenceCount = 1;
return o;
}
NPObject* NPN_RetainObjectProc(NPObject *npobj) {
if( npobj == NULL )
return NULL;
npobj->referenceCount++;
return npobj;
}
void NPN_ReleaseVariantValueProc(NPVariant *variant) {
//printf("ReleaseVariantValueProc\n");
switch( variant->type ) {
case NPVariantType_Null:
case NPVariantType_Void:
break;
case NPVariantType_String:
free( (char*)variant->value.stringValue.UTF8Characters );
variant->type = NPVariantType_Void;
break;
case NPVariantType_Object:
NPN_ReleaseObjectProc(variant->value.objectValue);
variant->type = NPVariantType_Void;
break;
default:
break;
}
}
void SetException( NPObject *npobj, const NPUTF8 *message ) {
printf("SetException %s\n",message);
}
NPError NPN_GetURLNotifyProc(NPP instance, const char* url, const char* window, void* notifyData) {
printf("[D] NPN_GetURLNotifyProc:%p, url: %s, window: %s\n", instance, url, window);
if (window && strlen(window)==6 && memcmp("_blank",window,6)==0) {
//system_launch_url(url);
pluginFuncs.urlnotify(instance,url,NPRES_DONE,notifyData);
} else if( memcmp(url,"javascript:",11) == 0 ) {
NPStream s;
uint16_t stype;
int success;
memset(&s,0,sizeof(NPStream));
s.url = strdup(url);
success = (pluginFuncs.newstream(instance,"text/html",&s,0,&stype) == NPERR_NO_ERROR);
if( success ) {
int pos = 0;
int size;
char buf[256];
sprintf(buf,"%X__flashplugin_unique__",(int_val)instance);
size = (int)strlen(buf);
s.end = size;
while( pos < size ) {
int len = pluginFuncs.writeready(instance,&s);
if( len <= 0 )
break;
if( len > size - pos )
len = size - pos;
len = pluginFuncs.write(instance,&s,pos,len,buf+pos);
if( len <= 0 )
break;
pos += len;
}
success = (pos == size);
}
pluginFuncs.urlnotify(instance,url,success?NPRES_DONE:NPRES_NETWORK_ERR,notifyData);
pluginFuncs.destroystream(instance,&s,NPRES_DONE);
free((void*)s.url);
} else {
NPStream stream;
uint16_t stype;// = NP_NORMAL;
memset(&stream,0,sizeof(NPStream));
stream.url = strdup(url);
stream.notifyData = notifyData;
pluginFuncs.newstream(instance,"application/x-shockwave-flash",&stream, 0, &stype);
//fprintf(stderr, "NPP: %p URL: %s\n", instance, url);
FILE *pp;
char buffer[8192];
pp = fopen(url,"rb");
int len;
while((len=fread(buffer, 1, sizeof(buffer), pp)) != 0) {
pluginFuncs.writeready(instance, &stream);
pluginFuncs.write(instance, &stream, 0, len, buffer);
}
fclose(pp);
pluginFuncs.destroystream(instance, &stream, NPRES_DONE);
free((void*)stream.url);
//pluginFuncs.urlnotify(instance, url, NPRES_DONE, notifyData);
}
return NPERR_NO_ERROR;
}
NPError NPN_GetURL( NPP instance, const char* url, const char* target ) {
printf( "GetURL %s\n", url );
if (target && strlen(target)==6 && memcmp("_blank",target,6)==0) {
//system_launch_url(url);
return NPERR_NO_ERROR;
}
return NPERR_NO_ERROR;
}
NPError NPN_PostURLNotify( NPP instance, const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) {
printf( "PostURLNotify (url) %s (target) %s (buf) %s[%d] (file) %i (nd) %p\n", url, target, buf, len, file, notifyData);
//url_process(instance,url,buf,len,notifyData);
return NPERR_NO_ERROR;
}
NPError NPN_PostURL( NPP instance, const char* url, const char* target, uint32_t len, const char* buf, NPBool file ) {
printf( "PostURL" );
return NPERR_NO_ERROR;
}
NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) {
printf("[D] NPN_RequestRead\n");
return NPERR_NO_ERROR;
}
NPError NewStream( NPP instance, NPMIMEType type, const char* target, NPStream** stream ) {
printf( "NewStream\n" );
return NPERR_NO_ERROR;
}
int32_t Write( NPP instance, NPStream* stream, int32_t len, void* buffer ) {
printf( "Write\n" );
return 0;
}
NPError DestroyStream( NPP instance, NPStream* stream, NPReason reason ) {
printf( "DestroyStream\n" );
return NPERR_NO_ERROR;
}
void _InvalidateRect( NPP instance, NPRect *invalidRect ) {
printf( "InvalidateRect\n" );
}
void InvalidateRegion( NPP instance, NPRegion invalidRegion ) {
printf( "InvalidateRegion\n" );
}
void ForceRedraw( NPP instance ) {
printf( "ForceRedraw\n" );
}
void initNPNetscapeFuncs(NPNetscapeFuncs *bFuncs) {
int i=0;
for(i=1; i<sizeof(*bFuncs)/sizeof(ssize_t); i++)
*(((ssize_t*)bFuncs)+i)=i+1000;
bFuncs->geturl=NPN_GetURL;
bFuncs->posturl=NPN_PostURL;
bFuncs->requestread=NPN_RequestRead;
bFuncs->newstream=NewStream;
bFuncs->write=Write;
bFuncs->destroystream=DestroyStream;
bFuncs->status=Status_;
bFuncs->uagent=NPN_UserAgentProc;
bFuncs->memalloc=malloc;
bFuncs->memfree=free;
bFuncs->memflush=MemFlush;
bFuncs->reloadplugins=ReloadPlugins;
bFuncs->getJavaEnv=GetJavaEnv;
bFuncs->getJavaPeer=GetJavaPeer;
bFuncs->geturlnotify=NPN_GetURLNotifyProc;
bFuncs->posturlnotify=NPN_PostURLNotify;
bFuncs->getvalue=NPN_GetValueProc;
bFuncs->setvalue=NPN_SetValueProc;
bFuncs->invalidaterect=_InvalidateRect;
bFuncs->invalidateregion=InvalidateRegion;
bFuncs->forceredraw=ForceRedraw;
bFuncs->getstringidentifier=NPN_GetStringIdentifierProc;
bFuncs->getstringidentifiers=GetStringIdentifiers;
bFuncs->getintidentifier=GetIntIdentifier;
bFuncs->identifierisstring=IdentifierIsString;
bFuncs->utf8fromidentifier=UTF8FromIdentifier;
bFuncs->intfromidentifier=IntFromIdentifier;
bFuncs->createobject=NPN_CreateObjectProc;
bFuncs->retainobject=NPN_RetainObjectProc;
bFuncs->releaseobject=NPN_ReleaseObjectProc;
bFuncs->invoke=NPN_InvokeProc;
bFuncs->invokeDefault=NPN_InvokeDefaultProc;
bFuncs->evaluate=Evaluate;
bFuncs->getproperty=NPN_GetPropertyProc;
bFuncs->setproperty=NPN_SetPropertyProc;
bFuncs->removeproperty=NPN_RemovePropertyProc;
bFuncs->hasproperty=NPN_HasPropertyProc;
bFuncs->hasmethod=NPN_HasMethodProc;
bFuncs->releasevariantvalue=NPN_ReleaseVariantValueProc;
bFuncs->setexception=SetException;
bFuncs->size= sizeof(bFuncs);
// bFuncs->version=(NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
bFuncs->version=20;
}
static void destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit ();
}
int main(int argc, char **argv)
{
if(argc < 2) {
printf("[-] Usage: %s <swfuri>\n", argv[0]);
exit(-1);
}
URL = argv[1];
gtk_init (&argc, &argv);
main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize (main_window, WINDOW_XSIZE, WINDOW_YSIZE);
g_signal_connect (G_OBJECT (main_window), "destroy", G_CALLBACK (destroy), NULL);
gtk_widget_realize(main_window);
//gtk_window_fullscreen((GtkWindow *)main_window);
gtk_widget_show_all(main_window);
printf("[+] created GTK widget\n");
flash_plugin_handle = loadFlashPluginSo();
loadNPEntryPoints(flash_plugin_handle);
printf("[+] initialized flash plugin entry points\n");
initNPNetscapeFuncs(&browserFuncs);
printf("[+] initialized browser functions\n");
checkError("NP_Initialize", iNP_Initialize(&browserFuncs, &pluginFuncs));
printPluginEntrypoints(&pluginFuncs);
printBrowserEntrypoints(&browserFuncs);
NPWindow *npwin = npwindow_construct(main_window);
printf("[+] created NPWindow widget\n");
NPP_t *instancep = (NPP_t *) malloc(sizeof(NPP_t));
memset(instancep,0,sizeof(sizeof(NPP_t)));
NPP instance = instancep;
NPSavedData* saved = (NPSavedData*) malloc(sizeof(NPSavedData));
memset(saved,0,sizeof(sizeof(NPSavedData)));
stream = npstream_construct();
uint16_t stype;
NPObject object;
char *xargv[]= {"allowscriptaccess", "quality", "wmode"};
char *xargm[]= {"always", "best", "direct"};
checkError("NPN_New", pluginFuncs.newp("application/x-shockwave-flash", instance, NP_EMBED, 3, xargv, xargm, 0));
checkError("NPN_GetValue NPPVpluginScriptableNPObject", pluginFuncs.getvalue(instance, NPPVpluginScriptableNPObject, &object));
checkError("NPN_SetWindow", pluginFuncs.setwindow(instance, npwin));
checkError("NPN_NewStream", pluginFuncs.newstream(instance, "application/x-shockwave-flash", stream, 0, &stype));
FILE *pp;
char buffer[8192];
pp = fopen(argv[1],"rb");
int len;
while((len=fread(buffer, 1, sizeof(buffer), pp)) != 0) {
pluginFuncs.writeready(instance, stream);
pluginFuncs.write(instance, stream, 0, len, buffer);
}
fclose(pp);
checkError("NPN_DestroyStream",pluginFuncs.destroystream(instance, stream, NPRES_DONE));
free(stream);
gtk_main();
checkError("NPN_Destroy",pluginFuncs.destroy(instance, &saved));
checkError("NP_Shutdown", iNP_Shutdown());
dlclose(flash_plugin_handle);
return 0;
}