예제 #1
0
파일: 06-onion.c 프로젝트: 1514louluo/onion
void t03_server_https(){
  INIT_LOCAL();
  CURL *curl=prepare_curl("https://localhost:8080");

  o=onion_new(O_ONE_LOOP | O_DETACH_LISTEN);
  onion_set_root_handler(o,onion_handler_new((void*)process_request,NULL,NULL));
  FAIL_IF_NOT_EQUAL_INT(onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, "mycert.pem", "mycert.pem"),0);
  FAIL_IF_NOT_EQUAL_INT(onion_listen(o),0);
  //do_petition_set(1,1,1,1);
  sleep(1);
  //FAIL_IF_EQUAL_INT(  curl_get_to_fail("http://localhost:8080"), HTTP_OK);
  sleep(1);
  FAIL_IF_NOT_EQUAL_INT(  curl_get(curl, "https://localhost:8080"), HTTP_OK);
  sleep(1);
  onion_free(o);

	curl_easy_cleanup(curl);
  END_LOCAL();
}
예제 #2
0
파일: 08-post.c 프로젝트: Andrepuel/onion
/// A BUG were detected: transformed \n to \r\n on files.
void t03_post_carriage_return_new_lines_file(){
	INIT_LOCAL();
	
	expected_post post={};;
	post.filename="file.dat";
	post.test_ok=0; // Not ok as not called processor yet
	post.tmpfilename=NULL;
	post.size=3;
	
	onion *server=onion_new(0);
	onion_listen_point *lp=onion_buffer_listen_point_new();
	onion_add_listen_point(server,NULL,NULL,lp);
	onion_set_root_handler(server, onion_handler_new((void*)&post_check,&post,NULL));
	
	onion_request *req=onion_request_new(lp);

#define POST_EMPTY "POST / HTTP/1.1\nContent-Type: multipart/form-data; boundary=end\nContent-Length:81\n\n--end\nContent-Disposition: text/plain; name=\"file\"; filename=\"file.dat\"\n\n\n\r\n\n--end--"
	//ONION_DEBUG("%s",POST_EMPTY);
	onion_request_write(req,POST_EMPTY,sizeof(POST_EMPTY));
	FAIL_IF_NOT_EQUAL(post.test_ok,1);
#undef POST_EMPTY

	onion_request_clean(req);
	post.test_ok=0; // Not ok as not called processor yet

#define POST_EMPTYR "POST / HTTP/1.1\r\nContent-Type: multipart/form-data; boundary=end\r\nContent-Length:85\r\n\r\n--end\r\nContent-Disposition: text/plain; name=\"file\"; filename=\"file.dat\"\r\n\r\n\n\r\n\r\n--end--"
	onion_request_write(req,POST_EMPTYR,sizeof(POST_EMPTYR));
	FAIL_IF_NOT_EQUAL(post.test_ok,1);
#undef POST_EMPTYR

	onion_request_free(req);

	if (post.tmpfilename){
		struct stat st;
		FAIL_IF_EQUAL(stat(post.tmpfilename,&st), 0); // Should not exist
		free(post.tmpfilename);
	}
	if (post.tmplink)
		free(post.tmplink);
	
	onion_free(server);
	END_LOCAL();
}
예제 #3
0
파일: multiport.c 프로젝트: msempere/onion
int main(int argc, char **argv){
	o=onion_new(O_THREADED);
	signal(SIGINT, free_onion);

	onion_set_root_handler(o, onion_handler_export_local_new("."));
	onion_add_listen_point(o, "localhost", "8080", onion_http_new());
	onion_add_listen_point(o, "localhost", "8081", onion_http_new());
	
	onion_add_listen_point(o, "localhost", "4443", onion_https_new(O_SSL_CERTIFICATE_KEY, "cert.pem", "cert.key"));
	
	
	
	/**
	onion_set_port(o, "localhost", "6121", onion_protocol_spdy());
	*/
	onion_listen(o);
	onion_free(o);
	return 0;
}
예제 #4
0
int main(int argc, char **argv){
	o=onion_new(O_THREADED);
	signal(SIGINT, free_onion);

	onion_set_root_handler(o, onion_handler_export_local_new("."));
	onion_add_listen_point(o, "localhost", "8080", onion_http_new());
	onion_add_listen_point(o, "localhost", "8081", onion_http_new());
#ifdef HAVE_GNUTLS
	onion_add_listen_point(o, "localhost", "4443", onion_https_new(O_SSL_CERTIFICATE_KEY, "cert.pem", "cert.key"));
#else
	ONION_WARNING("HTTPS support is not enabled. Recompile with gnutls");
#endif
	
	
	
	/**
	onion_set_port(o, "localhost", "6121", onion_protocol_spdy());
	*/
	onion_listen(o);
	onion_free(o);
	return 0;
}
예제 #5
0
파일: 08-post.c 프로젝트: Andrepuel/onion
/// There is a bug when posting large files. Introduced when change write 1 by 1, to write by blocks on the FILE parser
void t04_post_largefile(){
	INIT_LOCAL();
	
	int postfd=open(BIG_FILE, O_RDONLY);
	off_t filesize=lseek(postfd, 0, SEEK_END);
	lseek(postfd, 0, SEEK_SET);
	
	expected_post post={};;
	post.filename=BIG_FILE_BASE;
	post.test_ok=0; // Not ok as not called processor yet
	post.tmpfilename=NULL;
	post.size=filesize;
	
	onion *server=onion_new(0);
	onion_listen_point *lp=onion_buffer_listen_point_new();
	onion_add_listen_point(server,NULL,NULL,lp);
	onion_set_root_handler(server, onion_handler_new((void*)&post_check,&post,NULL));
	
	onion_request *req=onion_request_new(lp);

#define POST_HEADER "POST / HTTP/1.1\nContent-Type: multipart/form-data; boundary=end\nContent-Length: %d\n\n--end\nContent-Disposition: text/plain; name=\"file\"; filename=\"" BIG_FILE_BASE "\"\n\n"
	char tmp[1024];
	ONION_DEBUG("Post size is about %d",filesize+73);
	snprintf(tmp, sizeof(tmp), POST_HEADER, (int)filesize+73);
	ONION_DEBUG("%s",tmp);
	onion_request_write(req,tmp,strlen(tmp));
	
	int r=read(postfd, tmp, sizeof(tmp));
	while ( r>0 ){
		onion_request_write(req, tmp, r);
		r=read(postfd, tmp, sizeof(tmp));
	}
	
	onion_request_write(req,"\n--end--",8);
	FAIL_IF_NOT_EQUAL_INT(post.test_ok,1);
#undef POST_HEADER
	onion_request_clean(req);


	//post.test_ok=0; // Not ok as not called processor yet

	lseek(postfd, 0, SEEK_SET);

	int difffd=open(post.tmpfilename, O_RDONLY);
	FAIL_IF_NOT_EQUAL_INT(difffd,-1); // Orig file is removed at handler returns. But i have a copy
	difffd=open(post.tmplink, O_RDONLY);
	FAIL_IF_EQUAL_INT(difffd,-1);
	ONION_DEBUG("tmp filename %s",post.tmpfilename);
	int r1=1, r2=1;
	char c1=0, c2=0;
	int p=0;
	while ( r1 && r2 && c1==c2){
		r1=read(difffd, &c1, 1);
		r2=read(postfd, &c2, 1);
		//ONION_DEBUG("%d %d",c1,c2);
		FAIL_IF_NOT_EQUAL_INT(c1,c2);
		p++;
	}
	if ( r1 || r2 ){
		ONION_ERROR("At %d",p);
		FAIL_IF_NOT_EQUAL_INT(r1,0);
		FAIL_IF_NOT_EQUAL_INT(r2,0);
		FAIL_IF_NOT_EQUAL_INT(c1,c2);
		FAIL("Files are different");
	}
	else
		ONION_DEBUG("Files are ok");
	
	close(difffd);
	close(postfd);

	onion_request_free(req);

	if (post.tmpfilename){
		struct stat st;
		FAIL_IF_EQUAL(stat(post.tmpfilename,&st), 0); // Should not exist
	}
	
	onion_free(server);
	if (post.tmpfilename)
		free(post.tmpfilename);
	if (post.tmplink)
		free(post.tmplink);
	
	END_LOCAL();
}
예제 #6
0
void t03_handle_path_request(){
	INIT_LOCAL();

	onion *server=onion_new(0);
	onion_listen_point *lp=onion_buffer_listen_point_new();
	onion_add_listen_point(server, NULL, NULL, lp);

	onion_url *urls=onion_url_new();
	
	onion_url_add_static(urls, "^$", "Test index\n", HTTP_OK);
	onion_url_add_static(urls, "^index.html$", "Index test", 200);

	onion_url *pathu=onion_url_new();
	onion_handler *path=onion_url_to_handler(pathu);
	onion_url_add_url(pathu, "^test/", urls);
	onion_handler_add(path, onion_handler_static("Internal error", 500 ) );
	onion_set_root_handler(server, path);
	
	onion_request *request;
	onion_response *response;
	
	request=onion_request_new(lp);
	FILL(request,"GET / HTTP/1.1\n");
  onion_request_polish(request);
	response=onion_response_new(request);
	onion_handler_handle(path, request, response);
	onion_response_free(response);
	const char *buffer=onion_buffer_listen_point_get_buffer_data(request);
	FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.1 500 INTERNAL ERROR\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Content-Length: 14\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\nInternal error");
	onion_request_free(request);
	
	// gives error, as such url does not exist.
	request=onion_request_new(lp);
	FILL(request,"GET /test/ HTTP/1.1\n");
  onion_request_polish(request);
	response=onion_response_new(request);
	onion_handler_handle(path, request, response);
	onion_response_free(response);
	buffer=onion_buffer_listen_point_get_buffer_data(request);
	FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.1 200 OK\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Content-Length: 11\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\nTest index\n");
	onion_request_free(request);

	request=onion_request_new(lp);
	FILL(request,"GET /test/index.html HTTP/1.1\n");
  onion_request_polish(request);
	response=onion_response_new(request);
	onion_handler_handle(path, request, response);
	onion_response_free(response);
	buffer=onion_buffer_listen_point_get_buffer_data(request);
	FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.1 200 OK\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "Content-Length: 10\r\n");
	FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\nIndex test");
	onion_request_free(request);

	onion_free(server);

	END_LOCAL();
}
예제 #7
0
파일: onion.hpp 프로젝트: davidmoreno/onion
                /**
		* @short Sets the root handler.
		* 
		* The root handler is the entry point for all requests, so it should redirect to other handlers as needed.
		* Normally user dont set this handler manually, but use the Onion::Url constructor to set a Url handler as
		* the root one.
		*/
    void setRootHandler(Handler handler) {
      onion_set_root_handler(ptr, onion_handler_cpp_to_c(std::move(handler)));
    }
예제 #8
0
파일: oterm.c 프로젝트: angrygorilla/onion
int main(int argc, char **argv){
	char *port="8080";
	char *serverip="::";
	const char *command="/bin/bash";
	const char *certificatefile="/etc/pki/tls/certs/pound.pem";
	const char *keyfile="/etc/pki/tls/certs/pound.key";
	int error;
	int i;
	int ssl=1;
#ifdef HAVE_PAM
	int use_pam=1;
#endif
	
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--help")==0){
			show_help();
			exit(0);
		}
		else if(strcmp(argv[i],"-p")==0 || strcmp(argv[i],"--port")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the port number.");
				show_help();
				exit(1);
			}
			port=argv[++i];
			fprintf(stderr, "Using port %s\n",port);
		}
		else if(strcmp(argv[i],"-i")==0 || strcmp(argv[i],"--ip")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the ip address or hostname.");
				show_help();
				exit(1);
			}
			serverip=argv[++i];
			fprintf(stderr, "Using ip %s\n",serverip);
		}
		else if(strcmp(argv[i],"-c")==0 || strcmp(argv[i],"--cert")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the certificate filename");
				show_help();
				exit(1);
			}
			certificatefile=argv[++i];
			ONION_INFO("Using certificate %s",certificatefile);
		}
		else if(strcmp(argv[i],"-k")==0 || strcmp(argv[i],"--key")==0){
			if (i+1>argc){
				ONION_ERROR("Need to set the certificate key filename.");
				show_help();
				exit(1);
			}
			keyfile=argv[++i];
			ONION_INFO("Using certificate key %s",keyfile);
		}
		else if(strcmp(argv[i],"-x")==0 || strcmp(argv[i],"--exec")==0){
			if (i+1>argc){
				ONION_ERROR("Need the command to execute.");
				show_help();
				exit(1);
			}
			command=argv[++i];
			ONION_INFO("New terminal execute the command %s",command);
		}
		else if(strcmp(argv[i],"--no-ssl")==0){
			ssl=0;
			ONION_INFO("Disabling SSL!");
		}
#ifdef HAVE_PAM
		else if(strcmp(argv[i],"--no-pam")==0){
			use_pam=0;
			ONION_INFO("Disabling PAM!");
		}
#endif
	}
  o=onion_new(O_POOL|O_SYSTEMD);
  
	
	// I prepare the url handler, with static, uuid and term. Also added the empty rule that redirects to static/index.html
	onion_url *url=onion_url_new();
  onion_handler *term_handler=oterm_handler(o,command);
#ifdef HAVE_PAM
  if (use_pam){
    onion_url_add_handler(url, "^term/", onion_handler_auth_pam("Onion Terminal", "login", term_handler));
  }
  else
#endif
  {
    onion_url_add_with_data(url, "^term/", oterm_nopam, term_handler, NULL);
  }
  onion_url_add_with_data(url, "^uuid/", oterm_uuid, onion_handler_get_private_data(term_handler), NULL);
  
#ifdef __DEBUG__
	if (getenv("OTERM_DEBUG"))
		onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
	else
#endif
  {
    onion_url_add(url, "^static/", opack_static);
	}
  onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);

  srand(time(NULL));
	onion_set_root_handler(o, onion_url_to_handler(url));

	if (!(onion_flags(o)&O_SSL_AVAILABLE)){
		ONION_WARNING("SSL support is not available. Oterm is in unsecure mode!");
	}
	else if (ssl){ // Not necesary the else, as onion_use_certificate would just return an error. But then it will exit.
		error=onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, certificatefile, keyfile);
		if (error){
			ONION_ERROR("Cant set certificate and key files (%s, %s)",certificatefile, keyfile);
			show_help();
			exit(1);
		}
	}
	
	onion_set_port(o, port);
	onion_set_hostname(o, serverip);
  onion_set_timeout(o,5000);
	
	signal(SIGINT, free_onion);
	signal(SIGPIPE, SIG_IGN);
	fprintf(stderr, "Listening at %s\n",port);
	error=onion_listen(o);
	if (error){
		ONION_ERROR("Cant create the server: %s", strerror(errno));
	}
	
	onion_free(o);
	
	return 0;
}
예제 #9
0
파일: onion.hpp 프로젝트: 511860050/onion
 void setRootHandler(Handler *handler){
   onion_set_root_handler(ptr, handler->c_handler());
 }
예제 #10
0
void t01_call_otemplate(){
  INIT_LOCAL();


  onion *s=onion_new(0);

  onion_set_root_handler(s, onion_handler_new((void*)_13_otemplate_html_handler_page, NULL, NULL));
	onion_listen_point *lp=onion_buffer_listen_point_new();
	onion_add_listen_point(s,NULL,NULL,lp);

	struct tests_call_otemplate tests;

  onion_request *req=onion_request_new(lp);
  FAIL_IF_NOT_EQUAL_INT(onion_request_write0(req, "GET /\n\n"), OCS_REQUEST_READY);
  FAIL_IF_NOT_EQUAL_INT(onion_request_process(req), OCS_CLOSE_CONNECTION);

  ONION_INFO("Got %s",onion_buffer_listen_point_get_buffer_data(req));
  check_tests(onion_buffer_listen_point_get_buffer(req), &tests);

  FAIL_IF_NOT_EQUAL_INT(tests.ok_hello,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_list,0);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title,0);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title_title,0);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_encoding,0);


  onion_dict *d=onion_dict_new();
  onion_dict_add(d, "title", "TITLE",0);
  onion_dict_add(d, "hello", "SHOULD NOT APPEAR",0);
	onion_dict_add(d, "quoted", "<\"Hello>",0);

  onion_request_clean(req);
	onion_handler_free(onion_get_root_handler(s));
  onion_set_root_handler(s, onion_handler_new((void*)_13_otemplate_html_handler_page, d, NULL));
  FAIL_IF_NOT_EQUAL_INT(onion_request_write0(req, "GET /\n\n"), OCS_REQUEST_READY);
  FAIL_IF_NOT_EQUAL_INT(onion_request_process(req), OCS_CLOSE_CONNECTION);

  ONION_INFO("Got %s",onion_buffer_listen_point_get_buffer_data(req));
  check_tests(onion_buffer_listen_point_get_buffer(req), &tests);

  FAIL_IF_NOT_EQUAL_INT(tests.ok_hello,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_list,0);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title_title,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_encoding,1);


  onion_dict *d2=onion_dict_new();
  onion_dict_add(d2,"0","LIST 1",0);
  onion_dict_add(d2,"1","LIST 2",0);
  onion_dict_add(d2,"2","LIST 3",0);
  onion_dict_add(d,"list",d2, OD_DICT|OD_FREE_VALUE);

	onion_dict *f1=onion_dict_new();
	onion_dict *f2=onion_dict_new();
	onion_dict_add(f2, "0", "internal",0);
	onion_dict_add(f2, "1", "loop",0);
	onion_dict_add(f1, "loop", f2, OD_DICT|OD_FREE_VALUE);

	onion_dict_add(d, "loop", f1, OD_DICT|OD_FREE_VALUE);

  onion_request_clean(req);
	onion_handler_free(onion_get_root_handler(s));
  onion_set_root_handler(s, onion_handler_new((void*)_13_otemplate_html_handler_page, d, (void*)onion_dict_free));
    FAIL_IF_NOT_EQUAL_INT(onion_request_write0(req, "GET /\n\n"), OCS_REQUEST_READY);
  FAIL_IF_NOT_EQUAL_INT(onion_request_process(req), OCS_CLOSE_CONNECTION);
  check_tests(onion_buffer_listen_point_get_buffer(req), &tests);
  ONION_INFO("Got %s",onion_buffer_listen_point_get_buffer_data(req));

	FAIL_IF_NOT_EQUAL_INT(tests.ok_hello,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_list,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_title_title,1);
  FAIL_IF_NOT_EQUAL_INT(tests.ok_internal_loop,1);


  onion_request_free(req);
  onion_free(s);

  END_LOCAL();
}
예제 #11
0
int main(int argc, char **argv){
	//onion_log=onion_log_syslog;
	char *port="8080";
	char *hostname="::";
	const char *dirname=".";
#ifdef HAVE_WEBDAV
	int withwebdav=1;
#endif
	int i;
	for (i=1;i<argc;i++){
		if ((strcmp(argv[i],"--port")==0) || (strcmp(argv[i],"-p")==0)){
			port=argv[++i];
			ONION_INFO("Listening at port %s",port);
		}
		if ((strcmp(argv[i],"--listen")==0) || (strcmp(argv[i],"-l")==0)){
			hostname=argv[++i];
			ONION_INFO("Listening at hostname %s",hostname);
		}
		else if (strcmp(argv[i],"--help")==0 || strcmp(argv[i],"-h")==0){
			return show_help();
		}
#ifdef HAVE_WEBDAV
		else if (strcmp(argv[i],"--no-webdav")==0){
			ONION_INFO("WebDAV support disabled");
			withwebdav=0;
		}
#endif
		else{
			dirname=argv[i];
			ONION_INFO("Exporting directory %s", dirname);
		}
	}
	
	onion_handler *root=onion_handler_new((onion_handler_handler)fileserver_page, (void *)dirname, NULL);
#ifdef HAVE_WEBDAV
	if (withwebdav)
		onion_handler_add(root, onion_handler_webdav(dirname, NULL)); // fallback.
	else
#endif
		onion_handler_add(root, onion_handler_export_local_new(dirname));
		
// This is the root directory where the translations are.
#define W "."
	setenv("LANGUAGE","locale",1); // Remove LANGUAGE env var, set it to the locale name,
	setlocale(LC_ALL,""); 
	bindtextdomain("locale", W); // This is necesary because of the fake name
	bindtextdomain("es", W); // One per language supported.
	bindtextdomain("zh", W);
	bindtextdomain("fr", W);
	bindtextdomain("pl", W);
	textdomain("C"); // Default language
  // All is configured now, now in hands of dgettext(LANG, txt);
	
	o=onion_new(O_POOL);

	onion_set_root_handler(o, root);
	onion_set_port(o, port);
	onion_set_hostname(o, hostname);
	
	signal(SIGINT, free_onion);
	int error=onion_listen(o);
	if (error){
		perror("Cant create the server");
	}
	
	onion_free(o);
	 
	return 0;
}
예제 #12
0
int main(int argc, char **argv){
	char *port="8080";
	char *hostname="::";
	const char *dirname=".";
	const char *certfile="cert.pem";
	const char *pamname="login";
	int i;
	for (i=1;i<argc;i++){
		if ((strcmp(argv[i],"--port")==0) || (strcmp(argv[i],"-p")==0)){
			port=argv[++i];
			ONION_INFO("Listening at port %s",port);
		}
		if ((strcmp(argv[i],"--listen")==0) || (strcmp(argv[i],"-l")==0)){
			hostname=argv[++i];
			ONION_INFO("Listening at hostname %s",hostname);
		}
		else if (strcmp(argv[i],"--pem")==0){
			if (argc<i+1)
				return show_help();
			certfile=argv[++i];
			ONION_INFO("Certificate file set to %s",certfile);
		}
		else if (strcmp(argv[i],"--pam")==0){
			if (argc<i+1)
				return show_help();
			pamname=argv[++i];
			ONION_INFO("Pam name is now %s",pamname);
		}
		else if (strcmp(argv[i],"--help")==0 || strcmp(argv[i],"-h")==0){
			return show_help();
		}
		else
			dirname=argv[i];
	}
	
	upload_file_data data={
		dirname
	};

	onion_handler *root=onion_handler_new((void*)upload_file,(void*)&data,NULL);
	onion_handler *dir=onion_handler_export_local_new(argc==2 ? argv[1] : ".");
	onion_handler_export_local_set_footer(dir, upload_file_footer);
	onion_handler_add(dir, onion_handler_static("<h1>404 - File not found.</h1>", 404) );
	onion_handler_add(root,dir);
	onion_handler *pam=onion_handler_auth_pam("Onion Fileserver", pamname, root);

	
	o=onion_new(O_THREADED);
	onion_set_root_handler(o, pam);
	onion_set_certificate(o, O_SSL_CERTIFICATE_KEY, certfile, certfile);
	
	
	onion_set_port(o, port);
	onion_set_hostname(o, hostname);
	
	signal(SIGINT, free_onion);
	int error=onion_listen(o);
	if (error){
		perror("Cant create the server");
	}
	
	onion_free(o);
	 
	return 0;
}