void janus_videocall_create_session(janus_plugin_session *handle, int *error) {
	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
		*error = -1;
		return;
	}	
	janus_videocall_session *session = (janus_videocall_session *)g_malloc0(sizeof(janus_videocall_session));
	if(session == NULL) {
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
		*error = -2;
		return;
	}
	session->handle = handle;
	session->has_audio = FALSE;
	session->has_video = FALSE;
	session->audio_active = TRUE;
	session->video_active = TRUE;
	session->bitrate = 0;	/* No limit */
	session->peer = NULL;
	session->username = NULL;
	janus_mutex_init(&session->rec_mutex);
	session->destroyed = 0;
	g_atomic_int_set(&session->hangingup, 0);
	handle->plugin_handle = session;

	return;
}
Exemple #2
0
janus_text2pcap *janus_text2pcap_create(const char *dir, const char *filename, int truncate, gboolean text) {
	janus_text2pcap *tp;
	char newname[1024];
	char *fname;
	FILE *f;

	if(truncate < 0)
		return NULL;

	/* Copy given filename or generate a random one */
	if (filename == NULL) {
		g_snprintf(newname, sizeof(newname),
		    "janus-text2pcap-%"SCNu32".%s", janus_random_uint32(), text ? "txt" : "pcap");
	} else {
		g_strlcpy(newname, filename, sizeof(newname));
	}

	if(dir != NULL) {
		/* Create the directory, if needed */
		if(janus_mkdir(dir, 0755) < 0) {
			JANUS_LOG(LOG_ERR, "mkdir error: %d\n", errno);
			return NULL;
		}
		fname = g_strdup_printf("%s/%s", dir, newname);
	} else {
		fname = g_strdup(newname);
	}

	/* Try opening the file now */
	f = fopen(fname, "ab");
	if (f == NULL) {
		JANUS_LOG(LOG_ERR, "fopen(%s) error: %d\n", fname, errno);
		g_free(fname);
		return NULL;
	}

	/* Create the text2pcap instance */
	tp = g_malloc(sizeof(janus_text2pcap));
	tp->filename = fname;
	tp->file = f;
	tp->truncate = truncate;
	tp->text = text;
	g_atomic_int_set(&tp->writable, 1);
	janus_mutex_init(&tp->mutex);

	/* If we're saving to .pcap directly, generate a global header */
	if(!text) {
		janus_text2pcap_global_header header = {
			0xa1b2c3d4, 2, 4, 0, 0, 65535, 1
		};
		fwrite(&header, sizeof(char), sizeof(header), f);
	}

	return tp;
}
janus_transport_session *janus_transport_session_create(void *transport_p, void (*p_free)(void *)) {
	janus_transport_session *tp = g_malloc0(sizeof(janus_transport_session));
	if(tp == NULL)
		return NULL;
	tp->transport_p = transport_p;
	tp->p_free = p_free;
	g_atomic_int_set(&tp->destroyed, 0);
	janus_refcount_init(&tp->ref, janus_transport_session_free);
	janus_mutex_init(&tp->mutex);
	return tp;
}
/* Plugin implementation */
int janus_videocall_init(janus_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL) {
		janus_config_print(config);
		janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
		if(events != NULL && events->value != NULL)
			notify_events = janus_is_true(events->value);
		if(!notify_events && callback->events_is_enabled()) {
			JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOCALL_NAME);
		}
	}
	janus_config_destroy(config);
	config = NULL;
	
	sessions = g_hash_table_new(g_str_hash, g_str_equal);
	janus_mutex_init(&sessions_mutex);
	messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free);
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

	g_atomic_int_set(&initialized, 1);

	GError *error = NULL;
	/* Start the sessions watchdog */
	watchdog = g_thread_try_new("videocall watchdog", &janus_videocall_watchdog, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall watchdog thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	/* Launch the thread that will handle incoming messages */
	handler_thread = g_thread_try_new("videocall handler", janus_videocall_handler, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall handler thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME);
	return 0;
}
/* Plugin implementation */
int janus_echotest_init(janus_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_ECHOTEST_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL)
		janus_config_print(config);
	/* This plugin actually has nothing to configure... */
	janus_config_destroy(config);
	config = NULL;
	
	sessions = g_hash_table_new(NULL, NULL);
	janus_mutex_init(&sessions_mutex);
	messages = g_async_queue_new_full((GDestroyNotify) janus_echotest_message_free);
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;
	g_atomic_int_set(&initialized, 1);

	GError *error = NULL;
	/* Start the sessions watchdog */
	watchdog = g_thread_try_new("etest watchdog", &janus_echotest_watchdog, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the EchoTest watchdog thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	/* Launch the thread that will handle incoming messages */
	handler_thread = g_thread_try_new("janus echotest handler", janus_echotest_handler, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the EchoTest handler thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_ECHOTEST_NAME);
	return 0;
}
/* Plugin implementation */
int janus_videocall_init(janus_callbacks *callback, const char *config_path) {
	if(stopping) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	sprintf(filename, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL)
		janus_config_print(config);
	/* This plugin actually has nothing to configure... */
	janus_config_destroy(config);
	config = NULL;
	
	sessions = g_hash_table_new(g_str_hash, g_str_equal);
	janus_mutex_init(&sessions_mutex);
	messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free);
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

	initialized = 1;
	/* Launch the thread that will handle incoming messages */
	GError *error = NULL;
	handler_thread = g_thread_try_new("janus videocall handler", janus_videocall_handler, NULL, &error);
	if(error != NULL) {
		initialized = 0;
		/* Something went wrong... */
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME);
	return 0;
}
Exemple #7
0
/* Plugin implementation */
int janus_serial_init(janus_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_SERIAL_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL)
		janus_config_print(config);
	/* This plugin actually has nothing to configure... */
	janus_config_destroy(config);
	config = NULL;
	
	sessions = g_hash_table_new(NULL, NULL);
	janus_mutex_init(&sessions_mutex);
	messages = g_async_queue_new_full((GDestroyNotify) janus_serial_message_free);
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;
	g_atomic_int_set(&initialized, 1);

	GError *error = NULL;
	/* Start the sessions watchdog */
	watchdog = g_thread_try_new("serial watchdog", &janus_serial_watchdog, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Serial watchdog thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	/* Launch the thread that will handle incoming messages */
	handler_thread = g_thread_try_new("janus serial handler", janus_serial_handler, NULL, &error);
	if(error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the serial handler thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	
	// init part
	/* Open the file descriptor in non-blocking mode */
	if(fd = open(portname,O_RDWR | O_NOCTTY | O_NONBLOCK)){
    		//printf("stream aperto\n");
		JANUS_LOG(LOG_INFO, "stream aperto - Janus Serial\n");
	}else{
		//printf("errora nell'apertura dello stream\n");
		JANUS_LOG(LOG_INFO, "errore nell'apertura dello stream\n");
		return 0;
	}
	/* Set up the control structure */
	struct termios toptions;

	 /* Get currently set options for the tty */
	tcgetattr(fd, &toptions);

	/* Set custom options */

	/* 9600 baud */
	cfsetispeed(&toptions, B9600);
	cfsetospeed(&toptions, B9600);
	/* 8 bits, no parity, no stop bits */
	toptions.c_cflag &= ~PARENB;
	toptions.c_cflag &= ~CSTOPB;
	toptions.c_cflag &= ~CSIZE;
	toptions.c_cflag |= CS8;
	/* no hardware flow control */
	toptions.c_cflag &= ~CRTSCTS;
	/* enable receiver, ignore status lines */
	toptions.c_cflag |= CREAD | CLOCAL;
	/* disable input/output flow control, disable restart chars */
	toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
	/* disable canonical input, disable echo,
	disable visually erase chars,
	disable terminal-generated signals */
	toptions.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	/* disable output processing */
	toptions.c_oflag &= ~OPOST;

	/* wait for 24 characters to come in before read returns */
	toptions.c_cc[VMIN] = 12;
	/* no minimum time to wait before read returns */
	toptions.c_cc[VTIME] = 0;

	/* commit the options */
	tcsetattr(fd, TCSANOW, &toptions);

	/* Wait for the Arduino to reset */
	usleep(1000*1000);

	/* Flush anything already in the serial buffer */
	tcflush(fd, TCIFLUSH);
	
	write(fd,"k",1);	
	
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SERIAL_NAME);
	return 0;
}
/* Plugin implementation */
int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
	if(stopping) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	sprintf(filename, "%s/%s.cfg", config_path, JANUS_STREAMING_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL)
		janus_config_print(config);
	
	mountpoints = g_hash_table_new(NULL, NULL);
	/* Parse configuration to populate the mountpoints */
	if(config != NULL) {
		janus_config_category *cat = janus_config_get_categories(config);
		while(cat != NULL) {
			if(cat->name == NULL) {
				cat = cat->next;
				continue;
			}
			JANUS_LOG(LOG_VERB, "Adding stream '%s'\n", cat->name);
			janus_config_item *type = janus_config_get_item(cat, "type");
			if(type == NULL || type->value == NULL) {
				JANUS_LOG(LOG_VERB, "  -- Invalid type, skipping stream...\n");
				cat = cat->next;
				continue;
			}
			if(!strcasecmp(type->value, "rtp")) {
				/* RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */
				janus_config_item *id = janus_config_get_item(cat, "id");
				janus_config_item *desc = janus_config_get_item(cat, "description");
				janus_config_item *audio = janus_config_get_item(cat, "audio");
				janus_config_item *video = janus_config_get_item(cat, "video");
				janus_config_item *aport = janus_config_get_item(cat, "audioport");
				janus_config_item *acodec = janus_config_get_item(cat, "audiopt");
				janus_config_item *artpmap = janus_config_get_item(cat, "audiortpmap");
				janus_config_item *vport = janus_config_get_item(cat, "videoport");
				janus_config_item *vcodec = janus_config_get_item(cat, "videopt");
				janus_config_item *vrtpmap = janus_config_get_item(cat, "videortpmap");
				janus_streaming_mountpoint *live_rtp = calloc(1, sizeof(janus_streaming_mountpoint));
				if(live_rtp == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					continue;
				}
				if(id == NULL || id->value == NULL) {
					JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information...\n");
					cat = cat->next;
					continue;
				}
				gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes");
				gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes");
				if(!doaudio && !dovideo) {
					JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, no audio or video have to be streamed...\n");
					g_free(live_rtp);
					cat = cat->next;
					continue;
				}
				if(doaudio &&
						(aport == NULL || aport->value == NULL ||
						acodec == NULL || acodec->value == NULL ||
						artpmap == NULL || artpmap->value == NULL)) {
					JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for audio...\n");
					cat = cat->next;
					continue;
				}
				if(dovideo &&
						(vport == NULL || vport->value == NULL ||
						vcodec == NULL || vcodec->value == NULL ||
						vrtpmap == NULL || vrtpmap->value == NULL)) {
					JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for video...\n");
					cat = cat->next;
					continue;
				}
				JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled");
				live_rtp->name = g_strdup(cat->name);
				live_rtp->id = atoi(id->value);
				char *description = NULL;
				if(desc != NULL && desc->value != NULL)
					description = g_strdup(desc->value);
				else
					description = g_strdup(cat->name);
				live_rtp->description = description;
				live_rtp->active = FALSE;
				live_rtp->streaming_type = janus_streaming_type_live;
				live_rtp->streaming_source = janus_streaming_source_rtp;
				janus_streaming_rtp_source *live_rtp_source = calloc(1, sizeof(janus_streaming_rtp_source));
				if(live_rtp->name == NULL || description == NULL || live_rtp_source == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					if(live_rtp->name)
						g_free(live_rtp->name);
					if(description)
						g_free(description);
					if(live_rtp_source);
						g_free(live_rtp_source);
					g_free(live_rtp);
					continue;
				}
				live_rtp_source->audio_port = doaudio ? atoi(aport->value) : -1;
				live_rtp_source->video_port = dovideo ? atoi(vport->value) : -1;
				live_rtp->source = live_rtp_source;
				live_rtp->codecs.audio_pt = doaudio ? atoi(acodec->value) : -1;
				live_rtp->codecs.audio_rtpmap = doaudio ? g_strdup(artpmap->value) : NULL;
				live_rtp->codecs.video_pt = dovideo ? atoi(vcodec->value) : -1;
				live_rtp->codecs.video_rtpmap = dovideo ? g_strdup(vrtpmap->value) : NULL;
				live_rtp->listeners = NULL;
				g_hash_table_insert(mountpoints, GINT_TO_POINTER(live_rtp->id), live_rtp);
				g_thread_new(live_rtp->name, &janus_streaming_relay_thread, live_rtp);
			} else if(!strcasecmp(type->value, "live")) {
				/* a-Law file live source */
				janus_config_item *id = janus_config_get_item(cat, "id");
				janus_config_item *desc = janus_config_get_item(cat, "description");
				janus_config_item *file = janus_config_get_item(cat, "filename");
				janus_config_item *audio = janus_config_get_item(cat, "audio");
				janus_config_item *video = janus_config_get_item(cat, "video");
				if(id == NULL || id->value == NULL || file == NULL || file->value == NULL) {
					JANUS_LOG(LOG_ERR, "Can't add 'live' stream, missing mandatory information...\n");
					cat = cat->next;
					continue;
				}
				gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes");
				gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes");
				/* TODO We should support something more than raw a-Law and mu-Law streams... */
				if(!doaudio || dovideo) {
					JANUS_LOG(LOG_ERR, "Can't add 'live' stream, we only support audio file streaming right now...\n");
					cat = cat->next;
					continue;
				}
				if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
					JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
					cat = cat->next;
					continue;
				}
				janus_streaming_mountpoint *live_file = calloc(1, sizeof(janus_streaming_mountpoint));
				if(live_file == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					continue;
				}
				live_file->name = g_strdup(cat->name);
				live_file->id = atoi(id->value);
				char *description = NULL;
				if(desc != NULL && desc->value != NULL)
					description = g_strdup(desc->value);
				else
					description = g_strdup(cat->name);
				live_file->description = description;
				live_file->active = FALSE;
				live_file->streaming_type = janus_streaming_type_live;
				live_file->streaming_source = janus_streaming_source_file;
				janus_streaming_file_source *live_file_source = calloc(1, sizeof(janus_streaming_file_source));
				if(live_file->name == NULL || description == NULL || live_file_source == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					if(live_file->name)
						g_free(live_file->name);
					if(description)
						g_free(description);
					if(live_file_source);
						g_free(live_file_source);
					g_free(live_file);
					continue;
				}
				live_file_source->filename = g_strdup(file->value);
				live_file->source = live_file_source;
				live_file->codecs.audio_pt = strstr(file->value, ".alaw") ? 8 : 0;
				live_file->codecs.audio_rtpmap = strstr(file->value, ".alaw") ? "PCMA/8000" : "PCMU/8000";
				live_file->codecs.video_pt = -1;	/* FIXME We don't support video for this type yet */
				live_file->codecs.video_rtpmap = NULL;
				live_file->listeners = NULL;
				g_hash_table_insert(mountpoints, GINT_TO_POINTER(live_file->id), live_file);
				g_thread_new(live_file->name, &janus_streaming_filesource_thread, live_file);
			} else if(!strcasecmp(type->value, "ondemand")) {
				/* mu-Law file on demand source */
				janus_config_item *id = janus_config_get_item(cat, "id");
				janus_config_item *desc = janus_config_get_item(cat, "description");
				janus_config_item *file = janus_config_get_item(cat, "filename");
				janus_config_item *audio = janus_config_get_item(cat, "audio");
				janus_config_item *video = janus_config_get_item(cat, "video");
				if(id == NULL || id->value == NULL || file == NULL || file->value == NULL) {
					JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, missing mandatory information...\n");
					cat = cat->next;
					continue;
				}
				gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes");
				gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes");
				/* TODO We should support something more than raw a-Law and mu-Law streams... */
				if(!doaudio || dovideo) {
					JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, we only support audio file streaming right now...\n");
					cat = cat->next;
					continue;
				}
				if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
					JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
					cat = cat->next;
					continue;
				}
				janus_streaming_mountpoint *ondemand_file = calloc(1, sizeof(janus_streaming_mountpoint));
				if(ondemand_file == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					continue;
				}
				ondemand_file->name = g_strdup(cat->name);
				ondemand_file->id = atoi(id->value);
				char *description = NULL;
				if(desc != NULL && desc->value != NULL)
					description = g_strdup(desc->value);
				else
					description = g_strdup(cat->name);
				ondemand_file->description = description;
				ondemand_file->active = FALSE;
				ondemand_file->streaming_type = janus_streaming_type_on_demand;
				ondemand_file->streaming_source = janus_streaming_source_file;
				janus_streaming_file_source *ondemand_file_source = calloc(1, sizeof(janus_streaming_file_source));
				if(ondemand_file->name == NULL || description == NULL || ondemand_file_source == NULL) {
					JANUS_LOG(LOG_FATAL, "Memory error!\n");
					if(ondemand_file->name)
						g_free(ondemand_file->name);
					if(description)
						g_free(description);
					if(ondemand_file_source);
						g_free(ondemand_file_source);
					g_free(ondemand_file);
					continue;
				}
				ondemand_file_source->filename = g_strdup(file->value);
				ondemand_file->source = ondemand_file_source;
				ondemand_file->codecs.audio_pt = strstr(file->value, ".alaw") ? 8 : 0;
				ondemand_file->codecs.audio_rtpmap = strstr(file->value, ".alaw") ? "PCMA/8000" : "PCMU/8000";
				ondemand_file->codecs.video_pt = -1;	/* FIXME We don't support video for this type yet */
				ondemand_file->codecs.video_rtpmap = NULL;
				ondemand_file->listeners = NULL;
				g_hash_table_insert(mountpoints, GINT_TO_POINTER(ondemand_file->id), ondemand_file);
			}
			cat = cat->next;
		}
		/* Done */
		janus_config_destroy(config);
		config = NULL;
	}
	/* Show available mountpoints */
	GList *mountpoints_list = g_hash_table_get_values(mountpoints);
	GList *m = mountpoints_list;
	while(m) {
		janus_streaming_mountpoint *mp = (janus_streaming_mountpoint *)m->data;
		JANUS_LOG(LOG_VERB, "  ::: [%"SCNu64"][%s] %s (%s, %s)\n", mp->id, mp->name, mp->description,
			mp->streaming_type == janus_streaming_type_live ? "live" : "on demand",
			mp->streaming_source == janus_streaming_source_rtp ? "RTP source" : "file source");
		m = m->next;
	}
	g_list_free(mountpoints_list);

	sessions = g_hash_table_new(NULL, NULL);
	janus_mutex_init(&sessions_mutex);
	messages = g_queue_new();
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

	initialized = 1;
	/* Launch the thread that will handle incoming messages */
	GError *error = NULL;
	handler_thread = g_thread_try_new("janus streaming handler", janus_streaming_handler, NULL, &error);
	if(error != NULL) {
		initialized = 0;
		/* Something went wrong... */
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_STREAMING_NAME);
	return 0;
}
int janus_websockets_send_message(void *transport, void *request_id, gboolean admin, json_t *message) {
	if(message == NULL)
		return -1;
	if(transport == NULL) {
		json_decref(message);
		return -1;
	}
	/* Make sure this is not related to a closed /freed WebSocket session */
	janus_mutex_lock(&old_wss_mutex);
	janus_websockets_client *client = (janus_websockets_client *)transport;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	if(g_list_find(old_wss, client) != NULL || !client->wsi) {
#else
	if(g_list_find(old_wss, client) != NULL || !client->context || !client->wsi) {
#endif
		json_decref(message);
		message = NULL;
		transport = NULL;
		janus_mutex_unlock(&old_wss_mutex);
		return -1;
	}
	janus_mutex_lock(&client->mutex);
	/* Convert to string and enqueue */
	char *payload = json_dumps(message, json_format);
	g_async_queue_push(client->messages, payload);
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	lws_callback_on_writable(client->wsi);
#else
	libwebsocket_callback_on_writable(client->context, client->wsi);
#endif
	janus_mutex_unlock(&client->mutex);
	janus_mutex_unlock(&old_wss_mutex);
	json_decref(message);
	return 0;
}

void janus_websockets_session_created(void *transport, guint64 session_id) {
	/* We don't care */
}

void janus_websockets_session_over(void *transport, guint64 session_id, gboolean timeout) {
	if(transport == NULL || !timeout)
		return;
	/* We only care if it's a timeout: if so, close the connection */
	janus_websockets_client *client = (janus_websockets_client *)transport;
	/* Make sure this is not related to a closed WebSocket session */
	janus_mutex_lock(&old_wss_mutex);
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	if(g_list_find(old_wss, client) == NULL && client->wsi){
#else
	if(g_list_find(old_wss, client) == NULL && client->context && client->wsi){
#endif
		janus_mutex_lock(&client->mutex);
		client->session_timeout = 1;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		lws_callback_on_writable(client->wsi);
#else
		libwebsocket_callback_on_writable(client->context, client->wsi);
#endif
		janus_mutex_unlock(&client->mutex);
	}
	janus_mutex_unlock(&old_wss_mutex);
}


/* Thread */
void *janus_websockets_thread(void *data) {
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	struct lws_context *service = (struct lws_context *)data;
#else
	struct libwebsocket_context *service = (struct libwebsocket_context *)data;
#endif
	if(service == NULL) {
		JANUS_LOG(LOG_ERR, "Invalid service\n");
		return NULL;
	}

	const char *type = NULL;
	if(service == wss)
		type = "WebSocket (Janus API)";
	else if(service == swss)
		type = "Secure WebSocket (Janus API)";
	else if(service == admin_wss)
		type = "WebSocket (Admin API)";
	else if(service == admin_swss)
		type = "Secure WebSocket (Admin API)";

	JANUS_LOG(LOG_INFO, "%s thread started\n", type);

	while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
		/* libwebsockets is single thread, we cycle through events here */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		lws_service(service, 50);
#else
		libwebsocket_service(service, 50);
#endif
	}

	/* Get rid of the WebSockets server */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	lws_cancel_service(service);
#else
	libwebsocket_cancel_service(service);
#endif
	/* Done */
	JANUS_LOG(LOG_INFO, "%s thread ended\n", type);
	return NULL;
}


/* WebSockets */
static int janus_websockets_callback_http(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
	/* This endpoint cannot be used for HTTP */
	switch(reason) {
		case LWS_CALLBACK_HTTP:
			JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint\n");
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			lws_return_http_status(wsi, 403, NULL);
#else
			libwebsockets_return_http_status(this, wsi, 403, NULL);
#endif
			/* Close and free connection */
			return -1;
		case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
			if (!in) {
				JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint: no sub-protocol specified\n");
				return -1;
			}
			break;
		default:
			break;
	}
	return 0;
}

static int janus_websockets_callback_https(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
	/* We just forward the event to the HTTP handler */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	return janus_websockets_callback_http(wsi, reason, user, in, len);
#else
	return janus_websockets_callback_http(this, wsi, reason, user, in, len);
#endif
}

/* This callback handles Janus API requests */
static int janus_websockets_common_callback(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len, gboolean admin)
{
	const char *log_prefix = admin ? "AdminWSS" : "WSS";
	janus_websockets_client *ws_client = (janus_websockets_client *)user;
	switch(reason) {
		case LWS_CALLBACK_ESTABLISHED: {
			/* Is there any filtering we should apply? */
			char name[256], ip[256];
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, 256, ip, 256);
#else
			libwebsockets_get_peer_addresses(this, wsi, libwebsocket_get_socket_fd(wsi), name, 256, ip, 256);
#endif
			JANUS_LOG(LOG_VERB, "[%s-%p] WebSocket connection opened from %s by %s\n", log_prefix, wsi, ip, name);
			if(!janus_websockets_is_allowed(ip, admin)) {
				JANUS_LOG(LOG_ERR, "[%s-%p] IP %s is unauthorized to connect to the WebSockets %s API interface\n", log_prefix, wsi, ip, admin ? "Admin" : "Janus");
				/* Close the connection */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
				lws_callback_on_writable(wsi);
#else
				libwebsocket_callback_on_writable(this, wsi);
#endif
				return -1;
			}
			JANUS_LOG(LOG_VERB, "[%s-%p] WebSocket connection accepted\n", log_prefix, wsi);
			if(ws_client == NULL) {
				JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi);
				return -1;
			}
			/* Clean the old sessions list, in case this pointer was used before */
			janus_mutex_lock(&old_wss_mutex);
			if(g_list_find(old_wss, ws_client) != NULL)
				old_wss = g_list_remove(old_wss, ws_client);
			janus_mutex_unlock(&old_wss_mutex);
			/* Prepare the session */
#ifndef HAVE_LIBWEBSOCKETS_NEWAPI
			ws_client->context = this;
#endif
			ws_client->wsi = wsi;
			ws_client->messages = g_async_queue_new();
			ws_client->buffer = NULL;
			ws_client->buflen = 0;
			ws_client->bufpending = 0;
			ws_client->bufoffset = 0;
			ws_client->session_timeout = 0;
			ws_client->destroy = 0;
			janus_mutex_init(&ws_client->mutex);
			/* Let us know when the WebSocket channel becomes writeable */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			lws_callback_on_writable(wsi);
#else
			libwebsocket_callback_on_writable(this, wsi);
#endif
			JANUS_LOG(LOG_VERB, "[%s-%p]   -- Ready to be used!\n", log_prefix, wsi);
			/* Notify handlers about this new transport */
			if(notify_events && gateway->events_is_enabled()) {
				json_t *info = json_object();
				json_object_set_new(info, "event", json_string("connected"));
				json_object_set_new(info, "admin_api", admin ? json_true() : json_false());
				json_object_set_new(info, "ip", json_string(ip));
				gateway->notify_event(&janus_websockets_transport, ws_client, info);
			}
			return 0;
		}
		case LWS_CALLBACK_RECEIVE: {
			JANUS_LOG(LOG_HUGE, "[%s-%p] Got %zu bytes:\n", log_prefix, wsi, len);
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			if(ws_client == NULL || ws_client->wsi == NULL) {
#else
			if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
#endif
				JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi);
				return -1;
			}
			/* Is this a new message, or part of a fragmented one? */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			const size_t remaining = lws_remaining_packet_payload(wsi);
#else
			const size_t remaining = libwebsockets_remaining_packet_payload(wsi);
#endif
			if(ws_client->incoming == NULL) {
				JANUS_LOG(LOG_HUGE, "[%s-%p] First fragment: %zu bytes, %zu remaining\n", log_prefix, wsi, len, remaining);
				ws_client->incoming = g_malloc0(len+1);
				memcpy(ws_client->incoming, in, len);
				ws_client->incoming[len] = '\0';
				JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming);
			} else {
				size_t offset = strlen(ws_client->incoming);
				JANUS_LOG(LOG_HUGE, "[%s-%p] Appending fragment: offset %zu, %zu bytes, %zu remaining\n", log_prefix, wsi, offset, len, remaining);
				ws_client->incoming = g_realloc(ws_client->incoming, offset+len+1);
				memcpy(ws_client->incoming+offset, in, len);
				ws_client->incoming[offset+len] = '\0';
				JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming+offset);
			}
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			if(remaining > 0 || !lws_is_final_fragment(wsi)) {
#else
			if(remaining > 0 || !libwebsocket_is_final_fragment(wsi)) {
#endif
				/* Still waiting for some more fragments */
				JANUS_LOG(LOG_HUGE, "[%s-%p] Waiting for more fragments\n", log_prefix, wsi);
				return 0;
			}
			JANUS_LOG(LOG_HUGE, "[%s-%p] Done, parsing message: %zu bytes\n", log_prefix, wsi, strlen(ws_client->incoming));
			/* If we got here, the message is complete: parse the JSON payload */
			json_error_t error;
			json_t *root = json_loads(ws_client->incoming, 0, &error);
			g_free(ws_client->incoming);
			ws_client->incoming = NULL;
			/* Notify the core, passing both the object and, since it may be needed, the error */
			gateway->incoming_request(&janus_websockets_transport, ws_client, NULL, admin, root, &error);
			return 0;
		}
		case LWS_CALLBACK_SERVER_WRITEABLE: {
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			if(ws_client == NULL || ws_client->wsi == NULL) {
#else
			if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) {
#endif
				JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi);
				return -1;
			}
			if(!ws_client->destroy && !g_atomic_int_get(&stopping)) {
				janus_mutex_lock(&ws_client->mutex);
				/* Check if we have a pending/partial write to complete first */
				if(ws_client->buffer && ws_client->bufpending > 0 && ws_client->bufoffset > 0
						&& !ws_client->destroy && !g_atomic_int_get(&stopping)) {
					JANUS_LOG(LOG_HUGE, "[%s-%p] Completing pending WebSocket write (still need to write last %d bytes)...\n",
						log_prefix, wsi, ws_client->bufpending);
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
					int sent = lws_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
#else
					int sent = libwebsocket_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT);
#endif
					JANUS_LOG(LOG_HUGE, "[%s-%p]   -- Sent %d/%d bytes\n", log_prefix, wsi, sent, ws_client->bufpending);
					if(sent > -1 && sent < ws_client->bufpending) {
						/* We still couldn't send everything that was left, we'll try and complete this in the next round */
						ws_client->bufpending -= sent;
						ws_client->bufoffset += sent;
					} else {
						/* Clear the pending/partial write queue */
						ws_client->bufpending = 0;
						ws_client->bufoffset = 0;
					}
					/* Done for this round, check the next response/notification later */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
					lws_callback_on_writable(wsi);
#else
					libwebsocket_callback_on_writable(this, wsi);
#endif
					janus_mutex_unlock(&ws_client->mutex);
					return 0;
				}
				/* Shoot all the pending messages */
				char *response = g_async_queue_try_pop(ws_client->messages);
				if(response && !ws_client->destroy && !g_atomic_int_get(&stopping)) {
					/* Gotcha! */
					int buflen = LWS_SEND_BUFFER_PRE_PADDING + strlen(response) + LWS_SEND_BUFFER_POST_PADDING;
					if(ws_client->buffer == NULL) {
						/* Let's allocate a shared buffer */
						JANUS_LOG(LOG_HUGE, "[%s-%p] Allocating %d bytes (response is %zu bytes)\n", log_prefix, wsi, buflen, strlen(response));
						ws_client->buflen = buflen;
						ws_client->buffer = g_malloc0(buflen);
					} else if(buflen > ws_client->buflen) {
						/* We need a larger shared buffer */
						JANUS_LOG(LOG_HUGE, "[%s-%p] Re-allocating to %d bytes (was %d, response is %zu bytes)\n", log_prefix, wsi, buflen, ws_client->buflen, strlen(response));
						ws_client->buflen = buflen;
						ws_client->buffer = g_realloc(ws_client->buffer, buflen);
					}
					memcpy(ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, response, strlen(response));
					JANUS_LOG(LOG_HUGE, "[%s-%p] Sending WebSocket message (%zu bytes)...\n", log_prefix, wsi, strlen(response));
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
					int sent = lws_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
#else
					int sent = libwebsocket_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT);
#endif
					JANUS_LOG(LOG_HUGE, "[%s-%p]   -- Sent %d/%zu bytes\n", log_prefix, wsi, sent, strlen(response));
					if(sent > -1 && sent < (int)strlen(response)) {
						/* We couldn't send everything in a single write, we'll complete this in the next round */
						ws_client->bufpending = strlen(response) - sent;
						ws_client->bufoffset = LWS_SEND_BUFFER_PRE_PADDING + sent;
						JANUS_LOG(LOG_HUGE, "[%s-%p]   -- Couldn't write all bytes (%d missing), setting offset %d\n",
							log_prefix, wsi, ws_client->bufpending, ws_client->bufoffset);
					}
					/* We can get rid of the message */
					free(response);
					/* Done for this round, check the next response/notification later */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
					lws_callback_on_writable(wsi);
#else
					libwebsocket_callback_on_writable(this, wsi);
#endif
					janus_mutex_unlock(&ws_client->mutex);
					return 0;
				}
				janus_mutex_unlock(&ws_client->mutex);
			}
			return 0;
		}
		case LWS_CALLBACK_CLOSED: {
			JANUS_LOG(LOG_VERB, "[%s-%p] WS connection down, closing\n", log_prefix, wsi);
			janus_websockets_destroy_client(ws_client, wsi, log_prefix);
			JANUS_LOG(LOG_VERB, "[%s-%p]   -- closed\n", log_prefix, wsi);
			return 0;
		}
		case LWS_CALLBACK_WSI_DESTROY: {
			JANUS_LOG(LOG_VERB, "[%s-%p] WS connection down, destroying\n", log_prefix, wsi);
			janus_websockets_destroy_client(ws_client, wsi, log_prefix);
			JANUS_LOG(LOG_VERB, "[%s-%p]   -- destroyed\n", log_prefix, wsi);
			return 0;
		}
		default:
			if(wsi != NULL) {
				JANUS_LOG(LOG_HUGE, "[%s-%p] %d (%s)\n", log_prefix, wsi, reason, janus_websockets_reason_string(reason));
			} else {
				JANUS_LOG(LOG_HUGE, "[%s] %d (%s)\n", log_prefix, reason, janus_websockets_reason_string(reason));
			}
			break;
	}
	return 0;
}

/* This callback handles Janus API requests */
static int janus_websockets_callback(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	return janus_websockets_common_callback(wsi, reason, user, in, len, FALSE);
#else
	return janus_websockets_common_callback(this, wsi, reason, user, in, len, FALSE);
#endif
}

static int janus_websockets_callback_secure(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
	/* We just forward the event to the Janus API handler */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	return janus_websockets_callback(wsi, reason, user, in, len);
#else
	return janus_websockets_callback(this, wsi, reason, user, in, len);
#endif
}

/* This callback handles Admin API requests */
static int janus_websockets_admin_callback(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	return janus_websockets_common_callback(wsi, reason, user, in, len, TRUE);
#else
	return janus_websockets_common_callback(this, wsi, reason, user, in, len, TRUE);
#endif
}

static int janus_websockets_admin_callback_secure(
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
		struct lws *wsi,
		enum lws_callback_reasons reason,
#else
		struct libwebsocket_context *this,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason,
#endif
		void *user, void *in, size_t len)
{
	/* We just forward the event to the Admin API handler */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	return janus_websockets_admin_callback(wsi, reason, user, in, len);
#else
	return janus_websockets_admin_callback(this, wsi, reason, user, in, len);
#endif
}
/* Transport implementation */
int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
	JANUS_LOG(LOG_INFO, "libwebsockets >= 1.6 available, using new API\n");
#else
	JANUS_LOG(LOG_INFO, "libwebsockets < 1.6 available, using old API\n");
#endif

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL) {
		janus_config_print(config);

		/* Handle configuration */
		janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json");
		if(item && item->value) {
			/* Check how we need to format/serialize the JSON output */
			if(!strcasecmp(item->value, "indented")) {
				/* Default: indented, we use three spaces for that */
				json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
			} else if(!strcasecmp(item->value, "plain")) {
				/* Not indented and no new lines, but still readable */
				json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER;
			} else if(!strcasecmp(item->value, "compact")) {
				/* Compact, so no spaces between separators */
				json_format = JSON_COMPACT | JSON_PRESERVE_ORDER;
			} else {
				JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value);
				json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
			}
		}

		/* Check if we need to send events to handlers */
		janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
		if(events != NULL && events->value != NULL)
			notify_events = janus_is_true(events->value);
		if(!notify_events && callback->events_is_enabled()) {
			JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_WEBSOCKETS_NAME);
		}

		item = janus_config_get_item_drilldown(config, "general", "ws_logging");
		if(item && item->value) {
			ws_log_level = atoi(item->value);
			if(ws_log_level < 0)
				ws_log_level = 0;
		}
		JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level);
		lws_set_log_level(ws_log_level, NULL);
		old_wss = NULL;
		janus_mutex_init(&old_wss_mutex);

		/* Any ACL for either the Janus or Admin API? */
		item = janus_config_get_item_drilldown(config, "general", "ws_acl");
		if(item && item->value) {
			gchar **list = g_strsplit(item->value, ",", -1);
			gchar *index = list[0];
			if(index != NULL) {
				int i=0;
				while(index != NULL) {
					if(strlen(index) > 0) {
						JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index);
						janus_websockets_allow_address(g_strdup(index), FALSE);
					}
					i++;
					index = list[i];
				}
			}
			g_strfreev(list);
			list = NULL;
		}
		item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl");
		if(item && item->value) {
			gchar **list = g_strsplit(item->value, ",", -1);
			gchar *index = list[0];
			if(index != NULL) {
				int i=0;
				while(index != NULL) {
					if(strlen(index) > 0) {
						JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index);
						janus_websockets_allow_address(g_strdup(index), TRUE);
					}
					i++;
					index = list[i];
				}
			}
			g_strfreev(list);
			list = NULL;
		}

		/* Setup the Janus API WebSockets server(s) */
		item = janus_config_get_item_drilldown(config, "general", "ws");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "WebSockets server disabled\n");
		} else {
			int wsport = 8188;
			item = janus_config_get_item_drilldown(config, "general", "ws_port");
			if(item && item->value)
				wsport = atoi(item->value);
			char *interface = NULL;
			item = janus_config_get_item_drilldown(config, "general", "ws_interface");
			if(item && item->value)
				interface = (char *)item->value;
			char *ip = NULL;
			item = janus_config_get_item_drilldown(config, "general", "ws_ip");
			if(item && item->value) {
				ip = (char *)item->value;
				char *iface = janus_websockets_get_interface_name(ip);
				if(iface == NULL) {
					JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip);
				}
				ip = iface;
			}
			/* Prepare context */
			struct lws_context_creation_info info;
			memset(&info, 0, sizeof info);
			info.port = wsport;
			info.iface = ip ? ip : interface;
			info.protocols = wss_protocols;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			info.extensions = NULL;
#else
			info.extensions = libwebsocket_get_internal_extensions();
#endif
			info.ssl_cert_filepath = NULL;
			info.ssl_private_key_filepath = NULL;
			info.gid = -1;
			info.uid = -1;
			info.options = 0;
			/* Create the WebSocket context */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			wss = lws_create_context(&info);
#else
			wss = libwebsocket_create_context(&info);
#endif
			if(wss == NULL) {
				JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
			} else {
				JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport);
			}
			g_free(ip);
		}
		item = janus_config_get_item_drilldown(config, "general", "wss");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n");
		} else {
			int wsport = 8989;
			item = janus_config_get_item_drilldown(config, "general", "wss_port");
			if(item && item->value)
				wsport = atoi(item->value);
			char *interface = NULL;
			item = janus_config_get_item_drilldown(config, "general", "wss_interface");
			if(item && item->value)
				interface = (char *)item->value;
			char *ip = NULL;
			item = janus_config_get_item_drilldown(config, "general", "wss_ip");
			if(item && item->value) {
				ip = (char *)item->value;
				char *iface = janus_websockets_get_interface_name(ip);
				if(iface == NULL) {
					JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip);
				}
				ip = iface;
			}
			item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
			if(!item || !item->value) {
				JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
			} else {
				char *server_pem = (char *)item->value;
				char *server_key = (char *)item->value;
				item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
				if(item && item->value)
					server_key = (char *)item->value;
				JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
				/* Prepare secure context */
				struct lws_context_creation_info info;
				memset(&info, 0, sizeof info);
				info.port = wsport;
				info.iface = ip ? ip : interface;
				info.protocols = swss_protocols;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
				info.extensions = NULL;
#else
				info.extensions = libwebsocket_get_internal_extensions();
#endif
				info.ssl_cert_filepath = server_pem;
				info.ssl_private_key_filepath = server_key;
				info.gid = -1;
				info.uid = -1;
#if LWS_LIBRARY_VERSION_MAJOR >= 2
				info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#else
				info.options = 0;
#endif
				/* Create the secure WebSocket context */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
				swss = lws_create_context(&info);
#else
				swss = libwebsocket_create_context(&info);
#endif
				if(swss == NULL) {
					JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
				} else {
					JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport);
				}
				g_free(ip);
			}
		}
		/* Do the same for the Admin API, if enabled */
		item = janus_config_get_item_drilldown(config, "admin", "admin_ws");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n");
		} else {
			int wsport = 7188;
			item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port");
			if(item && item->value)
				wsport = atoi(item->value);
			char *interface = NULL;
			item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface");
			if(item && item->value)
				interface = (char *)item->value;
			char *ip = NULL;
			item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip");
			if(item && item->value) {
				ip = (char *)item->value;
				char *iface = janus_websockets_get_interface_name(ip);
				if(iface == NULL) {
					JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip);
				}
				ip = iface;
			}
			/* Prepare context */
			struct lws_context_creation_info info;
			memset(&info, 0, sizeof info);
			info.port = wsport;
			info.iface = ip ? ip : interface;
			info.protocols = admin_wss_protocols;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			info.extensions = NULL;
#else
			info.extensions = libwebsocket_get_internal_extensions();
#endif
			info.ssl_cert_filepath = NULL;
			info.ssl_private_key_filepath = NULL;
			info.gid = -1;
			info.uid = -1;
			info.options = 0;
			/* Create the WebSocket context */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
			admin_wss = lws_create_context(&info);
#else
			admin_wss = libwebsocket_create_context(&info);
#endif
			if(admin_wss == NULL) {
				JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
			} else {
				JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport);
			}
			g_free(ip);
		}
		item = janus_config_get_item_drilldown(config, "admin", "admin_wss");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n");
		} else {
			int wsport = 7989;
			item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port");
			if(item && item->value)
				wsport = atoi(item->value);
			char *interface = NULL;
			item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface");
			if(item && item->value)
				interface = (char *)item->value;
			char *ip = NULL;
			item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip");
			if(item && item->value) {
				ip = (char *)item->value;
				char *iface = janus_websockets_get_interface_name(ip);
				if(iface == NULL) {
					JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip);
				}
				ip = iface;
			}
			item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
			if(!item || !item->value) {
				JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
			} else {
				char *server_pem = (char *)item->value;
				char *server_key = (char *)item->value;
				item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
				if(item && item->value)
					server_key = (char *)item->value;
				JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
				/* Prepare secure context */
				struct lws_context_creation_info info;
				memset(&info, 0, sizeof info);
				info.port = wsport;
				info.iface = ip ? ip : interface;
				info.protocols = admin_swss_protocols;
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
				info.extensions = NULL;
#else
				info.extensions = libwebsocket_get_internal_extensions();
#endif
				info.ssl_cert_filepath = server_pem;
				info.ssl_private_key_filepath = server_key;
				info.gid = -1;
				info.uid = -1;
#if LWS_LIBRARY_VERSION_MAJOR >= 2
				info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#else
				info.options = 0;
#endif
				/* Create the secure WebSocket context */
#ifdef HAVE_LIBWEBSOCKETS_NEWAPI
				admin_swss = lws_create_context(&info);
#else
				admin_swss = libwebsocket_create_context(&info);
#endif
				if(admin_swss == NULL) {
					JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n");
				} else {
					JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport);
				}
				g_free(ip);
			}
		}
	}
	janus_config_destroy(config);
	config = NULL;
	if(!wss && !swss && !admin_wss && !admin_swss) {
		JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n");
		return -1;	/* No point in keeping the plugin loaded */
	}
	wss_janus_api_enabled = wss || swss;
	wss_admin_api_enabled = admin_wss || admin_swss;

	GError *error = NULL;
	/* Start the WebSocket service threads */
	if(wss != NULL) {
		wss_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wss, &error);
		if(!wss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(swss != NULL) {
		swss_thread = g_thread_try_new("sws thread", &janus_websockets_thread, swss, &error);
		if(!swss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(admin_wss != NULL) {
		admin_wss_thread = g_thread_try_new("admin ws thread", &janus_websockets_thread, admin_wss, &error);
		if(!admin_wss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(admin_swss != NULL) {
		admin_swss_thread = g_thread_try_new("admin sws thread", &janus_websockets_thread, admin_swss, &error);
		if(!admin_swss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}

	/* Done */
	g_atomic_int_set(&initialized, 1);
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME);
	return 0;
}
Exemple #11
0
/* DTLS-SRTP initialization */
gint janus_dtls_srtp_init(const char *server_pem, const char *server_key, const char *password) {
	const char *crypto_lib = NULL;
#if JANUS_USE_OPENSSL_PRE_1_1_API
#if defined(LIBRESSL_VERSION_NUMBER)
	crypto_lib = "LibreSSL";
#else
	crypto_lib = "OpenSSL pre-1.1.0";
#endif
	/* First of all make OpenSSL thread safe (see note above on issue #316) */
	janus_dtls_locks = g_malloc0(sizeof(*janus_dtls_locks) * CRYPTO_num_locks());
	int l=0;
	for(l = 0; l < CRYPTO_num_locks(); l++) {
		janus_mutex_init(&janus_dtls_locks[l]);
	}
	CRYPTO_THREADID_set_callback(janus_dtls_cb_openssl_threadid);
	CRYPTO_set_locking_callback(janus_dtls_cb_openssl_lock);
#else
	crypto_lib = "OpenSSL >= 1.1.0";
#endif
#ifdef HAVE_BORINGSSL
	crypto_lib = "BoringSSL";
#endif
	JANUS_LOG(LOG_INFO, "Crypto: %s\n", crypto_lib);
#ifndef HAVE_SRTP_AESGCM
	JANUS_LOG(LOG_WARN, "The libsrtp installation does not support AES-GCM profiles\n");
#endif

	/* Go on and create the DTLS context */
#if JANUS_USE_OPENSSL_PRE_1_1_API
	ssl_ctx = SSL_CTX_new(DTLSv1_method());
#else
	ssl_ctx = SSL_CTX_new(DTLS_method());
#endif
	if(!ssl_ctx) {
		JANUS_LOG(LOG_FATAL, "Ops, error creating DTLS context?\n");
		return -1;
	}
	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, janus_dtls_verify_callback);
	SSL_CTX_set_tlsext_use_srtp(ssl_ctx,
#ifdef HAVE_SRTP_AESGCM
		"SRTP_AEAD_AES_256_GCM:SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32");
#else
		"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32");
#endif

	if(!server_pem && !server_key) {
		JANUS_LOG(LOG_WARN, "No cert/key specified, autogenerating some...\n");
		if(janus_dtls_generate_keys(&ssl_cert, &ssl_key) != 0) {
			JANUS_LOG(LOG_FATAL, "Error generating DTLS key/certificate\n");
			return -2;
		}
	} else if(!server_pem || !server_key) {
		JANUS_LOG(LOG_FATAL, "DTLS certificate and key must be specified\n");
		return -2;
	} else if(janus_dtls_load_keys(server_pem, server_key, password, &ssl_cert, &ssl_key) != 0) {
		return -3;
	}

	if(!SSL_CTX_use_certificate(ssl_ctx, ssl_cert)) {
		JANUS_LOG(LOG_FATAL, "Certificate error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -4;
	}
	if(!SSL_CTX_use_PrivateKey(ssl_ctx, ssl_key)) {
		JANUS_LOG(LOG_FATAL, "Certificate key error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -5;
	}
	if(!SSL_CTX_check_private_key(ssl_ctx)) {
		JANUS_LOG(LOG_FATAL, "Certificate check error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -6;
	}
	SSL_CTX_set_read_ahead(ssl_ctx,1);

	unsigned int size;
	unsigned char fingerprint[EVP_MAX_MD_SIZE];
	if(X509_digest(ssl_cert, EVP_sha256(), (unsigned char *)fingerprint, &size) == 0) {
		JANUS_LOG(LOG_FATAL, "Error converting X509 structure (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -7;
	}
	char *lfp = (char *)&local_fingerprint;
	unsigned int i = 0;
	for(i = 0; i < size; i++) {
		g_snprintf(lfp, 4, "%.2X:", fingerprint[i]);
		lfp += 3;
	}
	*(lfp-1) = 0;
	JANUS_LOG(LOG_INFO, "Fingerprint of our certificate: %s\n", local_fingerprint);
	SSL_CTX_set_cipher_list(ssl_ctx, DTLS_CIPHERS);

	if(janus_dtls_bio_filter_init() < 0) {
		JANUS_LOG(LOG_FATAL, "Error initializing BIO filter\n");
		return -8;
	}

	/* Initialize libsrtp */
	if(srtp_init() != srtp_err_status_ok) {
		JANUS_LOG(LOG_FATAL, "Ops, error setting up libsrtp?\n");
		return 5;
	}
	return 0;
}
Exemple #12
0
/* Transport implementation */
int janus_pfunix_init(janus_transport_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_PFUNIX_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL) {
		/* Handle configuration */
		janus_config_print(config);

		janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json");
		if(item && item->value) {
			/* Check how we need to format/serialize the JSON output */
			if(!strcasecmp(item->value, "indented")) {
				/* Default: indented, we use three spaces for that */
				json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
			} else if(!strcasecmp(item->value, "plain")) {
				/* Not indented and no new lines, but still readable */
				json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER;
			} else if(!strcasecmp(item->value, "compact")) {
				/* Compact, so no spaces between separators */
				json_format = JSON_COMPACT | JSON_PRESERVE_ORDER;
			} else {
				JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value);
				json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
			}
		}

		/* Check if we need to send events to handlers */
		janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
		if(events != NULL && events->value != NULL)
			notify_events = janus_is_true(events->value);
		if(!notify_events && callback->events_is_enabled()) {
			JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_PFUNIX_NAME);
		}

		/* First of all, initialize the socketpair for writeable notifications */
		if(socketpair(PF_LOCAL, SOCK_STREAM, 0, write_fd) < 0) {
			JANUS_LOG(LOG_FATAL, "Error creating socket pair for writeable events: %d, %s\n", errno, strerror(errno));
			return -1;
		}

		/* Setup the Janus API Unix Sockets server(s) */
		item = janus_config_get_item_drilldown(config, "general", "enabled");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Janus API)\n");
		} else {
			item = janus_config_get_item_drilldown(config, "general", "path");
			char *pfname = (char *)(item && item->value ? item->value : NULL);
			item = janus_config_get_item_drilldown(config, "general", "type");
			const char *type = item && item->value ? item->value : "SOCK_SEQPACKET";
			dgram = FALSE;
			if(!strcasecmp(type, "SOCK_SEQPACKET")) {
				dgram = FALSE;
			} else if(!strcasecmp(type, "SOCK_DGRAM")) {
				dgram = TRUE;
			} else {
				JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type);
				type = "SOCK_SEQPACKET";
			}
			if(pfname == NULL) {
				JANUS_LOG(LOG_WARN, "No path configured, skipping Unix Sockets server (Janus API)\n");
			} else {
				JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Janus API)\n", type);
				pfd = janus_pfunix_create_socket(pfname, dgram);
			}
		}
		/* Do the same for the Admin API, if enabled */
		item = janus_config_get_item_drilldown(config, "admin", "admin_enabled");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Admin API)\n");
		} else {
			item = janus_config_get_item_drilldown(config, "admin", "admin_path");
			char *pfname = (char *)(item && item->value ? item->value : NULL);
			item = janus_config_get_item_drilldown(config, "admin", "admin_type");
			const char *type = item && item->value ? item->value : "SOCK_SEQPACKET";
			if(!strcasecmp(type, "SOCK_SEQPACKET")) {
				admin_dgram = FALSE;
			} else if(!strcasecmp(type, "SOCK_DGRAM")) {
				admin_dgram = TRUE;
			} else {
				JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type);
				type = "SOCK_SEQPACKET";
			}
			if(pfname == NULL) {
				JANUS_LOG(LOG_WARN, "No path configured, skipping Unix Sockets server (Admin API)\n");
			} else {
				JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Admin API)\n", type);
				admin_pfd = janus_pfunix_create_socket(pfname, admin_dgram);
			}
		}
	}
	janus_config_destroy(config);
	config = NULL;
	if(pfd < 0 && admin_pfd < 0) {
		JANUS_LOG(LOG_WARN, "No Unix Sockets server started, giving up...\n");
		return -1;	/* No point in keeping the plugin loaded */
	}

	/* Create a couple of hashtables for all clients */
	clients = g_hash_table_new(NULL, NULL);
	clients_by_fd = g_hash_table_new(NULL, NULL);
	clients_by_path = g_hash_table_new(g_str_hash, g_str_equal);
	janus_mutex_init(&clients_mutex);

	/* Start the Unix Sockets service thread */
	GError *error = NULL;
	pfunix_thread = g_thread_try_new("pfunix thread", &janus_pfunix_thread, NULL, &error);
	if(!pfunix_thread) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Unix Sockets thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}

	/* Done */
	g_atomic_int_set(&initialized, 1);
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_PFUNIX_NAME);
	return 0;
}
Exemple #13
0
/* Transport implementation */
int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config != NULL) {
		janus_config_print(config);

		/* Handle configuration */
		janus_config_item *item = janus_config_get_item_drilldown(config, "general", "ws_logging");
		if(item && item->value) {
			ws_log_level = atoi(item->value);
			if(ws_log_level < 0)
				ws_log_level = 0;
		}
		JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level);
		lws_set_log_level(ws_log_level, NULL);
		old_wss = NULL;
		janus_mutex_init(&old_wss_mutex);

		/* Any ACL for either the Janus or Admin API? */
		item = janus_config_get_item_drilldown(config, "general", "ws_acl");
		if(item && item->value) {
			gchar **list = g_strsplit(item->value, ",", -1);
			gchar *index = list[0];
			if(index != NULL) {
				int i=0;
				while(index != NULL) {
					if(strlen(index) > 0) {
						JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index);
						janus_websockets_allow_address(g_strdup(index), FALSE);
					}
					i++;
					index = list[i];
				}
			}
			g_strfreev(list);
			list = NULL;
		}
		item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl");
		if(item && item->value) {
			gchar **list = g_strsplit(item->value, ",", -1);
			gchar *index = list[0];
			if(index != NULL) {
				int i=0;
				while(index != NULL) {
					if(strlen(index) > 0) {
						JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index);
						janus_websockets_allow_address(g_strdup(index), TRUE);
					}
					i++;
					index = list[i];
				}
			}
			g_strfreev(list);
			list = NULL;
		}

		/* Setup the Janus API WebSockets server(s) */
		item = janus_config_get_item_drilldown(config, "general", "ws");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "WebSockets server disabled\n");
		} else {
			int wsport = 8188;
			item = janus_config_get_item_drilldown(config, "general", "ws_port");
			if(item && item->value)
				wsport = atoi(item->value);
			/* Prepare context */
			struct lws_context_creation_info info;
			memset(&info, 0, sizeof info);
			info.port = wsport;
			info.iface = NULL;
			info.protocols = wss_protocols;
			info.extensions = libwebsocket_get_internal_extensions();
			info.ssl_cert_filepath = NULL;
			info.ssl_private_key_filepath = NULL;
			info.gid = -1;
			info.uid = -1;
			info.options = 0;
			/* Create the WebSocket context */
			wss = libwebsocket_create_context(&info);
			if(wss == NULL) {
				JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n");
			} else {
				JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport);
			}
		}
		item = janus_config_get_item_drilldown(config, "general", "wss");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n");
		} else {
			int wsport = 8989;
			item = janus_config_get_item_drilldown(config, "general", "wss_port");
			if(item && item->value)
				wsport = atoi(item->value);
			item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
			if(!item || !item->value) {
				JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
			} else {
				char *server_pem = (char *)item->value;
				char *server_key = (char *)item->value;
				item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
				if(item && item->value)
					server_key = (char *)item->value;
				JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
				/* Prepare secure context */
				struct lws_context_creation_info info;
				memset(&info, 0, sizeof info);
				info.port = wsport;
				info.iface = NULL;
				info.protocols = swss_protocols;
				info.extensions = libwebsocket_get_internal_extensions();
				info.ssl_cert_filepath = server_pem;
				info.ssl_private_key_filepath = server_key;
				info.gid = -1;
				info.uid = -1;
				info.options = 0;
				/* Create the secure WebSocket context */
				swss = libwebsocket_create_context(&info);
				if(swss == NULL) {
					JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n");
				} else {
					JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport);
				}
			}
		}
		/* Do the same for the Admin API, if enabled */
		item = janus_config_get_item_drilldown(config, "admin", "admin_ws");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n");
		} else {
			int wsport = 7188;
			item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port");
			if(item && item->value)
				wsport = atoi(item->value);
			/* Prepare context */
			struct lws_context_creation_info info;
			memset(&info, 0, sizeof info);
			info.port = wsport;
			info.iface = NULL;
			info.protocols = admin_wss_protocols;
			info.extensions = libwebsocket_get_internal_extensions();
			info.ssl_cert_filepath = NULL;
			info.ssl_private_key_filepath = NULL;
			info.gid = -1;
			info.uid = -1;
			info.options = 0;
			/* Create the WebSocket context */
			admin_wss = libwebsocket_create_context(&info);
			if(admin_wss == NULL) {
				JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n");
			} else {
				JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport);
			}
		}
		item = janus_config_get_item_drilldown(config, "admin", "admin_wss");
		if(!item || !item->value || !janus_is_true(item->value)) {
			JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n");
		} else {
			int wsport = 7989;
			item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port");
			if(item && item->value)
				wsport = atoi(item->value);
			item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
			if(!item || !item->value) {
				JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n");
			} else {
				char *server_pem = (char *)item->value;
				char *server_key = (char *)item->value;
				item = janus_config_get_item_drilldown(config, "certificates", "cert_key");
				if(item && item->value)
					server_key = (char *)item->value;
				JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key);
				/* Prepare secure context */
				struct lws_context_creation_info info;
				memset(&info, 0, sizeof info);
				info.port = wsport;
				info.iface = NULL;
				info.protocols = admin_swss_protocols;
				info.extensions = libwebsocket_get_internal_extensions();
				info.ssl_cert_filepath = server_pem;
				info.ssl_private_key_filepath = server_key;
				info.gid = -1;
				info.uid = -1;
				info.options = 0;
				/* Create the secure WebSocket context */
				admin_swss = libwebsocket_create_context(&info);
				if(admin_swss == NULL) {
					JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n");
				} else {
					JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport);
				}
			}
		}
	}
	janus_config_destroy(config);
	config = NULL;
	if(!wss && !swss && !admin_wss && !admin_swss) {
		JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n");
		return -1;	/* No point in keeping the plugin loaded */
	}
	wss_janus_api_enabled = wss || swss;
	wss_admin_api_enabled = admin_wss || admin_swss;

	GError *error = NULL;
	/* Start the WebSocket service threads */
	if(wss != NULL) {
		wss_thread = g_thread_try_new("websockets thread", &janus_websockets_thread, wss, &error);
		if(!wss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(swss != NULL) {
		swss_thread = g_thread_try_new("secure websockets thread", &janus_websockets_thread, swss, &error);
		if(!swss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(admin_wss != NULL) {
		admin_wss_thread = g_thread_try_new("admin websockets thread", &janus_websockets_thread, admin_wss, &error);
		if(!admin_wss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}
	if(admin_swss != NULL) {
		admin_swss_thread = g_thread_try_new("secure admin websockets thread", &janus_websockets_thread, admin_swss, &error);
		if(!admin_swss_thread) {
			g_atomic_int_set(&initialized, 0);
			JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??");
			return -1;
		}
	}

	/* Done */
	g_atomic_int_set(&initialized, 1);
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME);
	return 0;
}
Exemple #14
0
void janus_turnrest_init(void) {
	/* Initialize libcurl, needed for contacting the TURN REST API backend */
	curl_global_init(CURL_GLOBAL_ALL);
	janus_mutex_init(&api_mutex);
}
Exemple #15
0
/* Transport implementation */
int janus_rabbitmq_init(janus_transport_callbacks *callback, const char *config_path) {
	if(g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if(callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* This is the callback we'll need to invoke to contact the Janus core */
	gateway = callback;

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.jcfg", config_path, JANUS_RABBITMQ_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if(config == NULL) {
		JANUS_LOG(LOG_WARN, "Couldn't find .jcfg configuration file (%s), trying .cfg\n", JANUS_RABBITMQ_PACKAGE);
		g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_RABBITMQ_PACKAGE);
		JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
		config = janus_config_parse(filename);
	}
	if(config != NULL)
		janus_config_print(config);
	janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
	janus_config_category *config_admin = janus_config_get_create(config, NULL, janus_config_type_category, "admin");

	janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "json");
	if(item && item->value) {
		/* Check how we need to format/serialize the JSON output */
		if(!strcasecmp(item->value, "indented")) {
			/* Default: indented, we use three spaces for that */
			json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
		} else if(!strcasecmp(item->value, "plain")) {
			/* Not indented and no new lines, but still readable */
			json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER;
		} else if(!strcasecmp(item->value, "compact")) {
			/* Compact, so no spaces between separators */
			json_format = JSON_COMPACT | JSON_PRESERVE_ORDER;
		} else {
			JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value);
			json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER;
		}
	}

	/* Check if we need to send events to handlers */
	janus_config_item *events = janus_config_get(config, config_general, janus_config_type_item, "events");
	if(events != NULL && events->value != NULL)
		notify_events = janus_is_true(events->value);
	if(!notify_events && callback->events_is_enabled()) {
		JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_RABBITMQ_NAME);
	}

	/* Handle configuration, starting from the server details */
	item = janus_config_get(config, config_general, janus_config_type_item, "host");
	if(item && item->value)
		rmqhost = g_strdup(item->value);
	else
		rmqhost = g_strdup("localhost");
	int rmqport = AMQP_PROTOCOL_PORT;
	item = janus_config_get(config, config_general, janus_config_type_item, "port");
	if(item && item->value)
		rmqport = atoi(item->value);

	/* Credentials and Virtual Host */
	item = janus_config_get(config, config_general, janus_config_type_item, "vhost");
	if(item && item->value)
		vhost = g_strdup(item->value);
	else
		vhost = g_strdup("/");
	item = janus_config_get(config, config_general, janus_config_type_item, "username");
	if(item && item->value)
		username = g_strdup(item->value);
	else
		username = g_strdup("guest");
	item = janus_config_get(config, config_general, janus_config_type_item, "password");
	if(item && item->value)
		password = g_strdup(item->value);
	else
		password = g_strdup("guest");

	/* SSL config*/
	gboolean ssl_enabled = FALSE;
	gboolean ssl_verify_peer = FALSE;
	gboolean ssl_verify_hostname = FALSE;
	item = janus_config_get(config, config_general, janus_config_type_item, "ssl_enabled");
	if(item == NULL) {
		/* Try legacy property */
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_enable");
		if (item && item->value) {
			JANUS_LOG(LOG_WARN, "Found deprecated 'ssl_enable' property, please update it to 'ssl_enabled' instead\n");
		}
	}
	if(!item || !item->value || !janus_is_true(item->value)) {
		JANUS_LOG(LOG_INFO, "RabbitMQ SSL support disabled\n");
	} else {
		ssl_enabled = TRUE;
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_cacert");
		if(item && item->value)
			ssl_cacert_file = g_strdup(item->value);
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_cert");
		if(item && item->value)
			ssl_cert_file = g_strdup(item->value);
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_key");
		if(item && item->value)
			ssl_key_file = g_strdup(item->value);
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_verify_peer");
		if(item && item->value && janus_is_true(item->value))
			ssl_verify_peer = TRUE;
		item = janus_config_get(config, config_general, janus_config_type_item, "ssl_verify_hostname");
		if(item && item->value && janus_is_true(item->value))
			ssl_verify_hostname = TRUE;
	}

	/* Now check if the Janus API must be supported */
	item = janus_config_get(config, config_general, janus_config_type_item, "enabled");
	if(item == NULL) {
		/* Try legacy property */
		item = janus_config_get(config, config_general, janus_config_type_item, "enable");
		if (item && item->value) {
			JANUS_LOG(LOG_WARN, "Found deprecated 'enable' property, please update it to 'enabled' instead\n");
		}
	}
	if(!item || !item->value || !janus_is_true(item->value)) {
		JANUS_LOG(LOG_WARN, "RabbitMQ support disabled (Janus API)\n");
	} else {
		/* Parse configuration */
		item = janus_config_get(config, config_general, janus_config_type_item, "to_janus");
		if(!item || !item->value) {
			JANUS_LOG(LOG_FATAL, "Missing name of incoming queue for RabbitMQ integration...\n");
			goto error;
		}
		to_janus = g_strdup(item->value);
		item = janus_config_get(config, config_general, janus_config_type_item, "from_janus");
		if(!item || !item->value) {
			JANUS_LOG(LOG_FATAL, "Missing name of outgoing queue for RabbitMQ integration...\n");
			goto error;
		}
		from_janus = g_strdup(item->value);
		item = janus_config_get(config, config_general, janus_config_type_item, "janus_exchange");
		if(!item || !item->value) {
			JANUS_LOG(LOG_INFO, "Missing name of outgoing exchange for RabbitMQ integration, using default\n");
		} else {
			janus_exchange = g_strdup(item->value);
		}
		if (janus_exchange == NULL) {
			JANUS_LOG(LOG_INFO, "RabbitMQ support for Janus API enabled, %s:%d (%s/%s)\n", rmqhost, rmqport, to_janus, from_janus);
		} else {
			JANUS_LOG(LOG_INFO, "RabbitMQ support for Janus API enabled, %s:%d (%s/%s) exch: (%s)\n", rmqhost, rmqport, to_janus, from_janus, janus_exchange);
		}
		rmq_janus_api_enabled = TRUE;
	}
	/* Do the same for the admin API */
	item = janus_config_get(config, config_admin, janus_config_type_item, "admin_enabled");
	if(item == NULL) {
		/* Try legacy property */
		item = janus_config_get(config, config_general, janus_config_type_item, "admin_enable");
		if (item && item->value) {
			JANUS_LOG(LOG_WARN, "Found deprecated 'admin_enable' property, please update it to 'admin_enabled' instead\n");
		}
	}
	if(!item || !item->value || !janus_is_true(item->value)) {
		JANUS_LOG(LOG_WARN, "RabbitMQ support disabled (Admin API)\n");
	} else {
		/* Parse configuration */
		item = janus_config_get(config, config_admin, janus_config_type_item, "to_janus_admin");
		if(!item || !item->value) {
			JANUS_LOG(LOG_FATAL, "Missing name of incoming queue for RabbitMQ integration...\n");
			goto error;
		}
		to_janus_admin = g_strdup(item->value);
		item = janus_config_get(config, config_admin, janus_config_type_item, "from_janus_admin");
		if(!item || !item->value) {
			JANUS_LOG(LOG_FATAL, "Missing name of outgoing queue for RabbitMQ integration...\n");
			goto error;
		}
		from_janus_admin = g_strdup(item->value);
		JANUS_LOG(LOG_INFO, "RabbitMQ support for Admin API enabled, %s:%d (%s/%s)\n", rmqhost, rmqport, to_janus_admin, from_janus_admin);
		rmq_admin_api_enabled = TRUE;
	}
	if(!rmq_janus_api_enabled && !rmq_admin_api_enabled) {
		JANUS_LOG(LOG_WARN, "RabbitMQ support disabled for both Janus and Admin API, giving up\n");
		goto error;
	} else {
		/* FIXME We currently support a single application, create a new janus_rabbitmq_client instance */
		rmq_client = g_malloc0(sizeof(janus_rabbitmq_client));
		/* Connect */
		rmq_client->rmq_conn = amqp_new_connection();
		amqp_socket_t *socket = NULL;
		int status;
		JANUS_LOG(LOG_VERB, "Creating RabbitMQ socket...\n");
		if (ssl_enabled) {
			socket = amqp_ssl_socket_new(rmq_client->rmq_conn);
			if(socket == NULL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error creating socket...\n");
				goto error;
			}
			if(ssl_verify_peer) {
				amqp_ssl_socket_set_verify_peer(socket, 1);
			} else {
				amqp_ssl_socket_set_verify_peer(socket, 0);
			}
			if(ssl_verify_hostname) {
				amqp_ssl_socket_set_verify_hostname(socket, 1);
			} else {
				amqp_ssl_socket_set_verify_hostname(socket, 0);
			}
			if(ssl_cacert_file) {
				status = amqp_ssl_socket_set_cacert(socket, ssl_cacert_file);
				if(status != AMQP_STATUS_OK) {
					JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error setting CA certificate... (%s)\n", amqp_error_string2(status));
					goto error;
				}
			}
			if(ssl_cert_file && ssl_key_file) {
				status = amqp_ssl_socket_set_key(socket, ssl_cert_file, ssl_key_file);
				if(status != AMQP_STATUS_OK) {
					JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error setting key... (%s)\n", amqp_error_string2(status));
					goto error;
				}
			}
		} else {
			socket = amqp_tcp_socket_new(rmq_client->rmq_conn);
			if(socket == NULL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error creating socket...\n");
				goto error;
			}
		}
		JANUS_LOG(LOG_VERB, "Connecting to RabbitMQ server...\n");
		status = amqp_socket_open(socket, rmqhost, rmqport);
		if(status != AMQP_STATUS_OK) {
			JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error opening socket... (%s)\n", amqp_error_string2(status));
			goto error;
		}
		JANUS_LOG(LOG_VERB, "Logging in...\n");
		amqp_rpc_reply_t result = amqp_login(rmq_client->rmq_conn, vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, username, password);
		if(result.reply_type != AMQP_RESPONSE_NORMAL) {
			JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error logging in... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
			goto error;
		}
		rmq_client->rmq_channel = 1;
		JANUS_LOG(LOG_VERB, "Opening channel...\n");
		amqp_channel_open(rmq_client->rmq_conn, rmq_client->rmq_channel);
		result = amqp_get_rpc_reply(rmq_client->rmq_conn);
		if(result.reply_type != AMQP_RESPONSE_NORMAL) {
			JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error opening channel... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
			goto error;
		}
		rmq_client->janus_exchange = amqp_empty_bytes;
		if(janus_exchange != NULL) {
			JANUS_LOG(LOG_VERB, "Declaring exchange...\n");
			rmq_client->janus_exchange = amqp_cstring_bytes(janus_exchange);
			amqp_exchange_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->janus_exchange, amqp_cstring_bytes(JANUS_RABBITMQ_EXCHANGE_TYPE), 0, 0, 0, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error diclaring exchange... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
		}
		rmq_client->janus_api_enabled = FALSE;
		if(rmq_janus_api_enabled) {
			rmq_client->janus_api_enabled = TRUE;
			JANUS_LOG(LOG_VERB, "Declaring incoming queue... (%s)\n", to_janus);
			rmq_client->to_janus_queue = amqp_cstring_bytes(to_janus);
			amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_queue, 0, 0, 0, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
			JANUS_LOG(LOG_VERB, "Declaring outgoing queue... (%s)\n", from_janus);
			rmq_client->from_janus_queue = amqp_cstring_bytes(from_janus);
			amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->from_janus_queue, 0, 0, 0, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
			amqp_basic_consume(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_queue, amqp_empty_bytes, 0, 1, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error consuming... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
		}
		rmq_client->admin_api_enabled = FALSE;
		if(rmq_admin_api_enabled) {
			rmq_client->admin_api_enabled = TRUE;
			JANUS_LOG(LOG_VERB, "Declaring incoming queue... (%s)\n", to_janus_admin);
			rmq_client->to_janus_admin_queue = amqp_cstring_bytes(to_janus_admin);
			amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_admin_queue, 0, 0, 0, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
			JANUS_LOG(LOG_VERB, "Declaring outgoing queue... (%s)\n", from_janus_admin);
			rmq_client->from_janus_admin_queue = amqp_cstring_bytes(from_janus_admin);
			amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->from_janus_admin_queue, 0, 0, 0, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
			amqp_basic_consume(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_admin_queue, amqp_empty_bytes, 0, 1, 0, amqp_empty_table);
			result = amqp_get_rpc_reply(rmq_client->rmq_conn);
			if(result.reply_type != AMQP_RESPONSE_NORMAL) {
				JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error consuming... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id));
				goto error;
			}
		}
		rmq_client->messages = g_async_queue_new();
		rmq_client->destroy = 0;
		/* Prepare the transport session (again, just one) */
		rmq_session = janus_transport_session_create(rmq_client, NULL);
		/* Start the threads */
		GError *error = NULL;
		rmq_client->in_thread = g_thread_try_new("rmq_in_thread", &janus_rmq_in_thread, rmq_client, &error);
		if(error != NULL) {
			/* Something went wrong... */
			JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the RabbitMQ incoming thread...\n", error->code, error->message ? error->message : "??");
			janus_transport_session_destroy(rmq_session);
			g_free(rmq_client);
			janus_config_destroy(config);
			return -1;
		}
		rmq_client->out_thread = g_thread_try_new("rmq_out_thread", &janus_rmq_out_thread, rmq_client, &error);
		if(error != NULL) {
			/* Something went wrong... */
			JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the RabbitMQ outgoing thread...\n", error->code, error->message ? error->message : "??");
			janus_transport_session_destroy(rmq_session);
			g_free(rmq_client);
			janus_config_destroy(config);
			return -1;
		}
		janus_mutex_init(&rmq_client->mutex);
		/* Done */
		JANUS_LOG(LOG_INFO, "Setup of RabbitMQ integration completed\n");
		/* Notify handlers about this new transport */
		if(notify_events && gateway->events_is_enabled()) {
			json_t *info = json_object();
			json_object_set_new(info, "event", json_string("connected"));
			gateway->notify_event(&janus_rabbitmq_transport, rmq_session, info);
		}
	}
	janus_config_destroy(config);
	config = NULL;

	/* Done */
	g_atomic_int_set(&initialized, 1);
	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_RABBITMQ_NAME);
	return 0;

error:
	/* If we got here, something went wrong */
	g_free(rmq_client);
	g_free(rmqhost);
	g_free(vhost);
	g_free(username);
	g_free(password);
	g_free(janus_exchange);
	g_free(to_janus);
	g_free(from_janus);
	g_free(to_janus_admin);
	g_free(from_janus_admin);
	g_free(ssl_cacert_file);
	g_free(ssl_cert_file);
	g_free(ssl_key_file);
	if(config)
		janus_config_destroy(config);
	return -1;
}
Exemple #16
0
janus_dtls_srtp *janus_dtls_srtp_create(void *ice_component, janus_dtls_role role) {
	janus_ice_component *component = (janus_ice_component *)ice_component;
	if(component == NULL) {
		JANUS_LOG(LOG_ERR, "No component, no DTLS...\n");
		return NULL;
	}
	janus_ice_stream *stream = component->stream;
	if(!stream) {
		JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n");
		return NULL;
	}
	janus_ice_handle *handle = stream->handle;
	if(!handle || !handle->agent) {
		JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n");
		return NULL;
	}
	janus_dtls_srtp *dtls = g_malloc0(sizeof(janus_dtls_srtp));
	if(dtls == NULL) {
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
		return NULL;
	}
	/* Create SSL context, at last */
	dtls->srtp_valid = 0;
	dtls->ssl = SSL_new(janus_dtls_get_ssl_ctx());
	if(!dtls->ssl) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"]     No component DTLS SSL session??\n", handle->handle_id);
		janus_dtls_srtp_destroy(dtls);
		return NULL;
	}
	SSL_set_ex_data(dtls->ssl, 0, dtls);
	SSL_set_info_callback(dtls->ssl, janus_dtls_callback);
	dtls->read_bio = BIO_new(BIO_s_mem());
	if(!dtls->read_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"]   Error creating read BIO!\n", handle->handle_id);
		janus_dtls_srtp_destroy(dtls);
		return NULL;
	}
	BIO_set_mem_eof_return(dtls->read_bio, -1);
	dtls->write_bio = BIO_new(BIO_s_mem());
	if(!dtls->write_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"]   Error creating write BIO!\n", handle->handle_id);
		janus_dtls_srtp_destroy(dtls);
		return NULL;
	}
	BIO_set_mem_eof_return(dtls->write_bio, -1);
	/* The write BIO needs our custom filter, or fragmentation won't work */
	dtls->filter_bio = BIO_new(BIO_janus_dtls_filter());
	if(!dtls->filter_bio) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"]   Error creating filter BIO!\n", handle->handle_id);
		janus_dtls_srtp_destroy(dtls);
		return NULL;
	}
	/* Chain filter and write BIOs */
	BIO_push(dtls->filter_bio, dtls->write_bio);
	/* Set the filter as the BIO to use for outgoing data */
	SSL_set_bio(dtls->ssl, dtls->read_bio, dtls->filter_bio);
	dtls->dtls_role = role;
	if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"]   Setting connect state (DTLS client)\n", handle->handle_id);
		SSL_set_connect_state(dtls->ssl);
	} else {
		JANUS_LOG(LOG_VERB, "[%"SCNu64"]   Setting accept state (DTLS server)\n", handle->handle_id);
		SSL_set_accept_state(dtls->ssl);
	}
	/* https://code.google.com/p/chromium/issues/detail?id=406458 
	 * Specify an ECDH group for ECDHE ciphers, otherwise they cannot be
	 * negotiated when acting as the server. Use NIST's P-256 which is
	 * commonly supported.
	 */
	EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
	if(ecdh == NULL) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"]   Error creating ECDH group!\n", handle->handle_id);
		janus_dtls_srtp_destroy(dtls);
		return NULL;
	}
	SSL_set_options(dtls->ssl, SSL_OP_SINGLE_ECDH_USE);
	SSL_set_tmp_ecdh(dtls->ssl, ecdh);
	EC_KEY_free(ecdh);
	dtls->ready = 0;
#ifdef HAVE_SCTP
	dtls->sctp = NULL;
#endif
	janus_mutex_init(&dtls->srtp_mutex);
	/* Done */
	dtls->dtls_connected = 0;
	dtls->component = component;
	return dtls;
}
Exemple #17
0
/* DTLS-SRTP initialization */
gint janus_dtls_srtp_init(const char* server_pem, const char* server_key) {
	/* FIXME First of all make OpenSSL thread safe (see note above on issue #316) */
	janus_dtls_locks = g_malloc0(sizeof(*janus_dtls_locks) * CRYPTO_num_locks());
	int l=0;
	for(l = 0; l < CRYPTO_num_locks(); l++) {
		janus_mutex_init(&janus_dtls_locks[l]);
	}
	CRYPTO_THREADID_set_callback(janus_dtls_cb_openssl_threadid);
	CRYPTO_set_locking_callback(janus_dtls_cb_openssl_lock);

	/* Go on and create the DTLS context */
	ssl_ctx = SSL_CTX_new(DTLSv1_method());
	if(!ssl_ctx) {
		JANUS_LOG(LOG_FATAL, "Ops, error creating DTLS context?\n");
		return -1;
	}
	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, janus_dtls_verify_callback);
	SSL_CTX_set_tlsext_use_srtp(ssl_ctx, "SRTP_AES128_CM_SHA1_80");	/* FIXME Should we support something else as well? */

	if (!server_pem && !server_key) {
		JANUS_LOG(LOG_WARN, "No cert/key specified, autogenerating some...\n");
		if (janus_dtls_generate_keys(&ssl_cert, &ssl_key) != 0) {
			JANUS_LOG(LOG_FATAL, "Error generating DTLS key/certificate\n");
			return -2;
		}
	} else if (!server_pem || !server_key) {
		JANUS_LOG(LOG_FATAL, "DTLS certificate and key must be specified\n");
		return -2;
	} else if (janus_dtls_load_keys(server_pem, server_key, &ssl_cert, &ssl_key) != 0) {
		return -3;
	}

	if(!SSL_CTX_use_certificate(ssl_ctx, ssl_cert)) {
		JANUS_LOG(LOG_FATAL, "Certificate error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -4;
	}
	if(!SSL_CTX_use_PrivateKey(ssl_ctx, ssl_key)) {
		JANUS_LOG(LOG_FATAL, "Certificate key error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -5;
	}
	if(!SSL_CTX_check_private_key(ssl_ctx)) {
		JANUS_LOG(LOG_FATAL, "Certificate check error (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -6;
	}
	SSL_CTX_set_read_ahead(ssl_ctx,1);

	unsigned int size;
	unsigned char fingerprint[EVP_MAX_MD_SIZE];
	if(X509_digest(ssl_cert, EVP_sha256(), (unsigned char *)fingerprint, &size) == 0) {
		JANUS_LOG(LOG_FATAL, "Error converting X509 structure (%s)\n", ERR_reason_error_string(ERR_get_error()));
		return -7;
	}
	char *lfp = (char *)&local_fingerprint;
	unsigned int i = 0;
	for(i = 0; i < size; i++) {
		g_snprintf(lfp, 4, "%.2X:", fingerprint[i]);
		lfp += 3;
	}
	*(lfp-1) = 0;
	JANUS_LOG(LOG_INFO, "Fingerprint of our certificate: %s\n", local_fingerprint);
	SSL_CTX_set_cipher_list(ssl_ctx, DTLS_CIPHERS);

	/* Initialize libsrtp */
	if(srtp_init() != err_status_ok) {
		JANUS_LOG(LOG_FATAL, "Ops, error setting up libsrtp?\n");
		return 5;
	}
	return 0;
}
/* Plugin implementation */
int janus_source_init(janus_callbacks *callback, const char *config_path) {
	if (g_atomic_int_get(&stopping)) {
		/* Still stopping from before */
		return -1;
	}
	if (callback == NULL || config_path == NULL) {
		/* Invalid arguments */
		return -1;
	}

	/* Read configuration */
	char filename[255];
	g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_SOURCE_PACKAGE);
	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
	janus_config *config = janus_config_parse(filename);
	if (config != NULL)
		janus_config_print(config);

	/* Parse configuration */
	if (config != NULL)
	{
		GList *cl = janus_config_get_categories(config);
		while (cl != NULL)
		{
			janus_config_category *cat = (janus_config_category *)cl->data;
			if (cat->name == NULL)
			{
				cl = cl->next;
				continue;
			}
			JANUS_LOG(LOG_VERB, "Parsing category '%s'\n", cat->name);
			janus_source_parse_ports_range(janus_config_get_item(cat, "udp_port_range"), &udp_min_port, &udp_max_port);
			janus_source_parse_keepalive_interval(janus_config_get_item(cat, "keepalive_interval"), &keepalive_interval);
			janus_source_parse_status_service_url(janus_config_get_item(cat, "keepalive_service_url"), &keepalive_service_url);
			janus_source_parse_status_service_url(janus_config_get_item(cat,"status_service_url"),&status_service_url);
			
			janus_source_parse_video_codec_priority(janus_config_get_item(cat, "video_codec_priority"));
			janus_source_parse_rtsp_interface_ip(janus_config_get_item(cat, "interface"),&rtsp_interface_ip);
			
			cl = cl->next;
		}
		janus_config_destroy(config);
		config = NULL;
	}

	if (udp_min_port <= 0 || udp_max_port <= 0) {
		udp_min_port = 4000;
		udp_max_port = 5000;
		JANUS_LOG(LOG_WARN, "Using default port range: %d-%d\n", udp_min_port, udp_max_port);
	}

	sessions = g_hash_table_new(NULL, NULL);
	janus_mutex_init(&sessions_mutex);
	messages = g_async_queue_new_full((GDestroyNotify)janus_source_message_free);
	/* This is the callback we'll need to invoke to contact the gateway */
	gateway = callback;
	g_atomic_int_set(&initialized, 1);

	GError *error = NULL;
	/* Start the sessions watchdog */
	watchdog = g_thread_try_new("source watchdog", &janus_source_watchdog, NULL, &error);
	if (error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SourcePlugin watchdog thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}

	gst_init(NULL, NULL);
	gst_debug_set_threshold_from_string(gst_debug_str, FALSE);

	curl_handle = curl_init();
	
	socket_utils_init(udp_min_port, udp_max_port);
	
	/* Launch the thread that will handle incoming messages */
	handler_thread = g_thread_try_new("janus source handler", janus_source_handler, NULL, &error);
	if (error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Source handler thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}
	
	/* Launch the thread that will handle rtsp clients */
	handler_rtsp_thread = g_thread_try_new("rtsp server", janus_source_rtsp_server_thread, NULL, &error); 
	if (error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Source rtsp server thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}

	/* Set PID */
	memset(&PID, 0, JANUS_PID_SIZE);	
	if (0 > janus_set_pid()) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got an error while plugin id initialize.");
		return -1;
	}

	/*Start the keepalive thread */
	keepalive = g_thread_try_new("source keepalive", &janus_source_keepalive, NULL, &error);
	if (error != NULL) {
		g_atomic_int_set(&initialized, 0);
		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SourcePlugin keepalive thread...\n", error->code, error->message ? error->message : "??");
		return -1;
	}

	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SOURCE_NAME);
	return 0;
}
Exemple #19
0
janus_recorder *janus_recorder_create(const char *dir, const char *codec, const char *filename) {
	janus_recorder_medium type = JANUS_RECORDER_AUDIO;
	if(codec == NULL) {
		JANUS_LOG(LOG_ERR, "Missing codec information\n");
		return NULL;
	}
	if(!strcasecmp(codec, "vp8") || !strcasecmp(codec, "vp9") || !strcasecmp(codec, "h264")) {
		type = JANUS_RECORDER_VIDEO;
	} else if(!strcasecmp(codec, "opus")
			|| !strcasecmp(codec, "g711") || !strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma")
			|| !strcasecmp(codec, "g722")) {
		type = JANUS_RECORDER_AUDIO;
	} else if(!strcasecmp(codec, "text")) {
		/* FIXME We only handle text on data channels, so that's the only thing we can save too */
		type = JANUS_RECORDER_DATA;
	} else {
		/* We don't recognize the codec: while we might go on anyway, we'd rather fail instead */
		JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
		return NULL;
	}
	/* Create the recorder */
	janus_recorder *rc = g_malloc0(sizeof(janus_recorder));
	rc->dir = NULL;
	rc->filename = NULL;
	rc->file = NULL;
	rc->codec = g_strdup(codec);
	rc->created = janus_get_real_time();
	const char *rec_dir = NULL;
	const char *rec_file = NULL;
	char *copy_for_parent = NULL;
	char *copy_for_base = NULL;
	/* Check dir and filename values */
	if (filename != NULL) {
		/* Helper copies to avoid overwriting */
		copy_for_parent = g_strdup(filename);
		copy_for_base = g_strdup(filename);
		/* Get filename parent folder */
		const char *filename_parent = dirname(copy_for_parent);
		/* Get filename base file */
		const char *filename_base = basename(copy_for_base);
		if (!dir) {
			/* If dir is NULL we have to create filename_parent and filename_base */
			rec_dir = filename_parent;
			rec_file = filename_base;
		} else {
			/* If dir is valid we have to create dir and filename*/
			rec_dir = dir;
			rec_file = filename;
			if (strcasecmp(filename_parent, ".") || strcasecmp(filename_base, filename)) {
				JANUS_LOG(LOG_WARN, "Unsupported combination of dir and filename %s %s\n", dir, filename);
			}
		}
	}
	if(rec_dir != NULL) {
		/* Check if this directory exists, and create it if needed */
		struct stat s;
		int err = stat(rec_dir, &s);
		if(err == -1) {
			if(ENOENT == errno) {
				/* Directory does not exist, try creating it */
				if(janus_mkdir(rec_dir, 0755) < 0) {
					JANUS_LOG(LOG_ERR, "mkdir error: %d\n", errno);
					return NULL;
				}
			} else {
				JANUS_LOG(LOG_ERR, "stat error: %d\n", errno);
				return NULL;
			}
		} else {
			if(S_ISDIR(s.st_mode)) {
				/* Directory exists */
				JANUS_LOG(LOG_VERB, "Directory exists: %s\n", rec_dir);
			} else {
				/* File exists but it's not a directory? */
				JANUS_LOG(LOG_ERR, "Not a directory? %s\n", rec_dir);
				return NULL;
			}
		}
	}
	char newname[1024];
	memset(newname, 0, 1024);
	if(rec_file == NULL) {
		/* Choose a random username */
		if(!rec_tempname) {
			/* Use .mjr as an extension right away */
			g_snprintf(newname, 1024, "janus-recording-%"SCNu32".mjr", janus_random_uint32());
		} else {
			/* Append the temporary extension to .mjr, we'll rename when closing */
			g_snprintf(newname, 1024, "janus-recording-%"SCNu32".mjr.%s", janus_random_uint32(), rec_tempext);
		}
	} else {
		/* Just append the extension */
		if(!rec_tempname) {
			/* Use .mjr as an extension right away */
			g_snprintf(newname, 1024, "%s.mjr", rec_file);
		} else {
			/* Append the temporary extension to .mjr, we'll rename when closing */
			g_snprintf(newname, 1024, "%s.mjr.%s", rec_file, rec_tempext);
		}
	}
	/* Try opening the file now */
	if(rec_dir == NULL) {
		rc->file = fopen(newname, "wb");
	} else {
		char path[1024];
		memset(path, 0, 1024);
		g_snprintf(path, 1024, "%s/%s", rec_dir, newname);
		rc->file = fopen(path, "wb");
	}
	if(rc->file == NULL) {
		JANUS_LOG(LOG_ERR, "fopen error: %d\n", errno);
		return NULL;
	}
	if(rec_dir)
		rc->dir = g_strdup(rec_dir);
	rc->filename = g_strdup(newname);
	rc->type = type;
	/* Write the first part of the header */
	fwrite(header, sizeof(char), strlen(header), rc->file);
	g_atomic_int_set(&rc->writable, 1);
	/* We still need to also write the info header first */
	g_atomic_int_set(&rc->header, 0);
	janus_mutex_init(&rc->mutex);
	/* Done */
	g_atomic_int_set(&rc->destroyed, 0);
	janus_refcount_init(&rc->ref, janus_recorder_free);
	g_free(copy_for_parent);
	g_free(copy_for_base);
	return rc;
}
Exemple #20
0
janus_recorder *janus_recorder_create(const char *dir, const char *codec, const char *filename) {
	janus_recorder_medium type = JANUS_RECORDER_AUDIO;
	if(codec == NULL) {
		JANUS_LOG(LOG_ERR, "Missing codec information\n");
		return NULL;
	}
	if(!strcasecmp(codec, "vp8") || !strcasecmp(codec, "vp9") || !strcasecmp(codec, "h264")) {
		type = JANUS_RECORDER_VIDEO;
		if(!strcasecmp(codec, "vp9")) {
			JANUS_LOG(LOG_WARN, "The post-processor currently doesn't support VP9: recording anyway for the future\n");
		}
	} else if(!strcasecmp(codec, "opus") || !strcasecmp(codec, "g711") || !strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma")) {
		type = JANUS_RECORDER_AUDIO;
		if(!strcasecmp(codec, "pcmu") || !strcasecmp(codec, "pcma"))
			codec = "g711";
	} else if(!strcasecmp(codec, "text")) {
		/* FIXME We only handle text on data channels, so that's the only thing we can save too */
		type = JANUS_RECORDER_DATA;
	} else {
		/* We don't recognize the codec: while we might go on anyway, we'd rather fail instead */
		JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
		return NULL;
	}
	/* Create the recorder */
	janus_recorder *rc = g_malloc0(sizeof(janus_recorder));
	if(rc == NULL) {
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
		return NULL;
	}
	rc->dir = NULL;
	rc->filename = NULL;
	rc->file = NULL;
	rc->codec = g_strdup(codec);
	rc->created = janus_get_real_time();
	if(dir != NULL) {
		/* Check if this directory exists, and create it if needed */
		struct stat s;
		int err = stat(dir, &s);
		if(err == -1) {
			if(ENOENT == errno) {
				/* Directory does not exist, try creating it */
				if(janus_mkdir(dir, 0755) < 0) {
					JANUS_LOG(LOG_ERR, "mkdir error: %d\n", errno);
					return NULL;
				}
			} else {
				JANUS_LOG(LOG_ERR, "stat error: %d\n", errno);
				return NULL;
			}
		} else {
			if(S_ISDIR(s.st_mode)) {
				/* Directory exists */
				JANUS_LOG(LOG_VERB, "Directory exists: %s\n", dir);
			} else {
				/* File exists but it's not a directory? */
				JANUS_LOG(LOG_ERR, "Not a directory? %s\n", dir);
				return NULL;
			}
		}
	}
	char newname[1024];
	memset(newname, 0, 1024);
	if(filename == NULL) {
		/* Choose a random username */
		g_snprintf(newname, 1024, "janus-recording-%"SCNu32".mjr", janus_random_uint32());
	} else {
		/* Just append the extension */
		g_snprintf(newname, 1024, "%s.mjr", filename);
	}
	/* Try opening the file now */
	if(dir == NULL) {
		rc->file = fopen(newname, "wb");
	} else {
		char path[1024];
		memset(path, 0, 1024);
		g_snprintf(path, 1024, "%s/%s", dir, newname);
		rc->file = fopen(path, "wb");
	}
	if(rc->file == NULL) {
		JANUS_LOG(LOG_ERR, "fopen error: %d\n", errno);
		return NULL;
	}
	if(dir)
		rc->dir = g_strdup(dir);
	rc->filename = g_strdup(newname);
	rc->type = type;
	/* Write the first part of the header */
	fwrite(header, sizeof(char), strlen(header), rc->file);
	rc->writable = 1;
	/* We still need to also write the info header first */
	rc->header = 0;
	janus_mutex_init(&rc->mutex);
	return rc;
}
Exemple #21
0
janus_sctp_association *janus_sctp_association_create(void *dtls, uint64_t handle_id, uint16_t udp_port) {
	if(dtls == NULL || udp_port == 0)
		return NULL;
	
	/* usrsctp provides UDP encapsulation of SCTP, but we need these messages to
	 * be encapsulated in DTLS and actually sent/received by libnice, and not by
	 * usrsctp itself... as such, we make use of the AF_CONN approach */

	janus_sctp_association *sctp = (janus_sctp_association *)calloc(1, sizeof(janus_sctp_association));
	if(sctp == NULL) {
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
		return NULL;
	}
	sctp->dtls = dtls;
	sctp->handle_id = handle_id;
	sctp->ready = FALSE;	/* We wait for the association setup, for this */
	sctp->local_port = 5000;	/* FIXME We always use this one */
	sctp->remote_port = udp_port;
	sctp->sock = NULL;

	struct socket *sock = NULL;
	unsigned int i = 0;
	struct sockaddr_conn sconn;

	/* Now go on with SCTP */
	janus_sctp_channel *channel = NULL;

	for(i = 0; i < NUMBER_OF_CHANNELS; i++) {
		channel = &(sctp->channels[i]);
		channel->id = i;
		channel->state = DATA_CHANNEL_CLOSED;
		channel->pr_policy = SCTP_PR_SCTP_NONE;
		channel->pr_value = 0;
		channel->stream = 0;
		channel->unordered = 0;
		channel->flags = 0;
	}
	for(i = 0; i < NUMBER_OF_STREAMS; i++) {
		sctp->stream_channel[i] = NULL;
		sctp->stream_buffer[i] = 0;
	}
	sctp->stream_buffer_counter = 0;
	sctp->sock = NULL;
	janus_mutex_init(&sctp->mutex);

	usrsctp_register_address((void *)sctp);
	usrsctp_sysctl_set_sctp_ecn_enable(0);
	if((sock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, janus_sctp_incoming_data, NULL, 0, (void *)sctp)) == NULL) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error creating usrsctp socket...\n", handle_id);
		free(sctp);
		sctp = NULL;
		return NULL;
	}
	/* Set SO_LINGER */
	struct linger linger_opt;
	linger_opt.l_onoff = 1;
	linger_opt.l_linger = 0;
	if(usrsctp_setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt))) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] setsockopt error: SO_LINGER\n", handle_id);
		free(sctp);
		sctp = NULL;
		return NULL;
	}
	/* Allow resetting streams */
	struct sctp_assoc_value av;
	av.assoc_id = SCTP_ALL_ASSOC;
	av.assoc_value = 1;
	if(usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(struct sctp_assoc_value)) < 0) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] setsockopt error: SCTP_ENABLE_STREAM_RESET\n", handle_id);
		free(sctp);
		sctp = NULL;
		return NULL;
	}
	/* Disable Nagle */
	uint32_t nodelay = 1;
	if(usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] setsockopt error: SCTP_NODELAY\n", handle_id);
		free(sctp);
		sctp = NULL;
		return NULL;
	}	
	/* Enable the events of interest */
	struct sctp_event event;
	memset(&event, 0, sizeof(event));
	event.se_assoc_id = SCTP_ALL_ASSOC;
	event.se_on = 1;
	for(i = 0; i < sizeof(event_types)/sizeof(uint16_t); i++) {
		event.se_type = event_types[i];
		if(usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) {
			JANUS_LOG(LOG_ERR, "[%"SCNu64"] setsockopt error: SCTP_EVENT\n", handle_id);
			free(sctp);
			sctp = NULL;
			return NULL;
		}
	}
	/* Configure our INIT message */
	struct sctp_initmsg initmsg;
	memset(&initmsg, 0, sizeof(struct sctp_initmsg));
	initmsg.sinit_num_ostreams = 16;	/* What Firefox says in the INIT (Chrome says 1023) */
	initmsg.sinit_max_instreams = 2048;	/* What both Chrome and Firefox say in the INIT */
	if(usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] setsockopt error: SCTP_INITMSG\n", handle_id);
		free(sctp);
		sctp = NULL;
		return NULL;
	}
	/* Bind our side of the communication, using AF_CONN as we're doing the actual delivery ourselves */
	memset(&sconn, 0, sizeof(struct sockaddr_conn));
	sconn.sconn_family = AF_CONN;
	sconn.sconn_port = htons(sctp->local_port);
	sconn.sconn_addr = (void *)sctp;
	if(usrsctp_bind(sock, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn)) < 0) {
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error binding client on port %"SCNu16"\n", handle_id, sctp->local_port);
		free(sctp);
		sctp = NULL;
		return NULL;
	}

#ifdef DEBUG_SCTP
	char debug_file[1024];
	g_snprintf(debug_file, 1024, "%s/sctp-debug-%"SCNu64".txt", debug_folder, handle_id);
	sctp->debug_dump = fopen(debug_file, "wt");
#endif

	/* We're done for now, the setup is done elsewhere */
	janus_mutex_lock(&sctp->mutex);
	sctp->sock = sock;
	sctp->in_messages = g_queue_new();
	sctp->out_messages = g_queue_new();
	GError *error = NULL;
	sctp->thread = g_thread_try_new("JanusSCTP", &janus_sctp_thread, sctp, &error);
	if(error != NULL) {
		/* Something went wrong... */
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Got error %d (%s) trying to launch the SCTP thread...\n", handle_id, error->code, error->message ? error->message : "??");
		g_queue_free(sctp->in_messages);
		sctp->in_messages = NULL;
		g_queue_free(sctp->out_messages);
		sctp->out_messages = NULL;
		g_free(sctp);
		sctp = NULL;
		return NULL;
	}
	janus_mutex_unlock(&sctp->mutex);
	return sctp;
}