diff --git a/Makefile.am b/Makefile.am
index 5be271da4d80cd2280500e835b8c7e596e9c6afa..62ef80a601d5e7e77af54ae79422fcddcbed08cd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,8 @@
 ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS)
 
 SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \
-		coreapi console gtk share scripts
+	coreapi console gtk share scripts daemon
+	
 
 
 
diff --git a/configure.ac b/configure.ac
index ce360445100e6653a255d51eb880cf4b7e368728..82c4e64a52bbe7ee9d397b1370b215ea132b00b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -600,6 +600,7 @@ coreapi/help/Makefile
 coreapi/help/Doxyfile
 gtk/Makefile
 console/Makefile
+daemon/Makefile
 share/Makefile
 share/C/Makefile
 share/fr/Makefile
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a644c9823dbf681e463dba167d7df53027bf66c7
--- /dev/null
+++ b/daemon/Makefile.am
@@ -0,0 +1,25 @@
+
+
+bin_PROGRAMS=linphone-daemon linphone-daemon-pipetest
+
+linphone_daemon_SOURCES=daemon.cc
+
+linphone_daemon_pipetest_SOURCES=daemon-pipetest.c
+
+linphone_daemon_pipetest_LDADD=$(ORTP_LIBS)
+
+linphone_daemon_LDADD=$(top_builddir)/coreapi/liblinphone.la $(READLINE_LIBS)  \
+		$(MEDIASTREAMER_LIBS) \
+		$(ORTP_LIBS) \
+		$(SPEEX_LIBS) \
+		$(OSIP_LIBS) 
+
+AM_CFLAGS=$(READLINE_CFLAGS) -DIN_LINPHONE $(STRICT_OPTIONS)
+AM_CXXFLAGS=$(READLINE_CXXFLAGS) -DIN_LINPHONE $(STRICT_OPTIONS)
+
+INCLUDES = \
+	-I$(top_srcdir)\
+	-I$(top_srcdir)/coreapi\
+	$(ORTP_CFLAGS) \
+	$(MEDIASTREAMER_CFLAGS)
+
diff --git a/daemon/daemon-pipetest.c b/daemon/daemon-pipetest.c
new file mode 100644
index 0000000000000000000000000000000000000000..2da3f77e69b211e76f58fa9bedec45cff2116339
--- /dev/null
+++ b/daemon/daemon-pipetest.c
@@ -0,0 +1,59 @@
+#define _GNU_SOURCE
+#include <fcntl.h>
+
+#include <poll.h>
+
+
+
+#include "ortp/ortp.h"
+
+static int running=1;
+
+int main(int argc, char *argv[]){
+	int fd=ortp_client_pipe_connect("ha-linphone");
+	struct pollfd pfds[2]={{0}};
+	char buf[4096];
+
+	ortp_init();
+	ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
+	if (fd==-1){
+		ortp_error("Could not connect to control pipe: %s",strerror(errno));
+		return -1;
+	}
+	pfds[0].fd=fd;
+	pfds[0].events=POLLIN;
+	pfds[1].fd=1;
+	pfds[1].events=POLLIN;
+	while (running){
+		int err;
+		int bytes;
+		err=poll(pfds,2,-1);
+		if (err>0){
+			/*splice to stdout*/
+			if (pfds[0].revents & POLLIN){
+				if ((bytes=read(pfds[0].fd,buf,sizeof(buf)))>0){
+					if (write(0,buf,bytes)==-1){
+						ortp_error("Fail to write to stdout?");
+						break;
+					}
+					fprintf(stdout,"\n");		
+				}else if (bytes==0){
+					break;
+				}
+			}
+			/*splice from stdin to pipe */
+			if (pfds[1].revents & POLLIN){
+				if ((bytes=read(pfds[1].fd,buf,sizeof(buf)))>0){
+					if (write(pfds[0].fd,buf,bytes)==-1){
+						ortp_error("Fail to write to unix socket");
+						break;
+					}		
+				}else if (bytes==0){
+					break;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
diff --git a/daemon/daemon.cc b/daemon/daemon.cc
new file mode 100644
index 0000000000000000000000000000000000000000..325869fcfeb29749c3d1350d0b0f237f81976587
--- /dev/null
+++ b/daemon/daemon.cc
@@ -0,0 +1,503 @@
+
+#include "linphonecore.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <poll.h>
+
+#include <iostream>
+#include <sstream>
+#include <algorithm>
+#include <list>
+#include <queue>
+
+using namespace std;
+
+
+class Daemon;
+
+class DaemonCommand{
+	public:
+		virtual void exec(Daemon *app, const char *args)=0;
+		bool matches(const char *name)const;
+		const std::string &getProto()const{
+			return mProto;		
+		}
+		const std::string &getHelp()const{
+			return mHelp;
+		}
+	protected:
+		DaemonCommand(const char *name, const char *proto, const char *help);
+		const std::string mName;
+		const std::string mProto;
+		const std::string mHelp;
+};
+
+class Response{
+	public:
+		enum Status { Ok, Error};
+		virtual ~Response(){
+		}
+		Response() : mStatus(Ok){
+		}
+		Response(const char *errormsg) : mStatus(Error), mReason(errormsg){
+		}
+		void setStatus(Status st){
+			mStatus=st;
+		}
+		void setReason(const char *reason){
+			mReason=reason;
+		}
+		void setBody(const char *body){
+			mBody=body;
+		}
+		const std::string &getBody()const{
+			return mBody;
+		}
+		virtual int toBuf(char *dst, int dstlen)const{
+			int i=0;
+			i+=snprintf(dst+i,dstlen-i,"Status: %s\n",mStatus==Ok ? "Ok" : "Error");
+			if (mReason.size()>0){
+				i+=snprintf(dst+i,dstlen-i,"Reason: %s\n",mReason.c_str());
+			}
+			if (mBody.size()>0){
+				i+=snprintf(dst+i,dstlen-i,"\n%s\n",mBody.c_str());
+			}
+			return i;
+		}
+	private:
+		Status mStatus;
+		string mReason;
+		string mBody;
+};
+
+class EventResponse : public Response{
+	public:
+		EventResponse(LinphoneCall *call, LinphoneCallState state);
+	private:
+};
+
+class Daemon{
+	friend class DaemonCommand;
+	public:
+		typedef Response::Status Status;
+		Daemon(const char *config_path, bool using_pipes);
+		~Daemon();
+		int run();
+		void quit();
+		void sendResponse(const Response &resp);
+		LinphoneCore *getCore();
+		const list<DaemonCommand*> &getCommandList()const;
+		LinphoneCall *findCall(int id);
+		bool pullEvent();
+		static int getCallId(LinphoneCall *call);
+		void setCallId(LinphoneCall *call);
+	private:
+		
+		static void callStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state, const char *msg);
+		static int readlineHook();
+		void callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg);
+		void execCommand(const char *cl);
+		void initReadline();
+		char *readPipe(char *buffer, int buflen);
+		void iterate();
+		void initCommands();
+		LinphoneCore *mLc;
+		std::list<DaemonCommand*> mCommands;
+		queue<EventResponse*> mEventQueue;
+		int mServerFd;
+		int mChildFd;
+		string mHistfile;
+		bool mRunning;
+		static Daemon *sZis;
+		static int sCallIds;
+		static const int sLineSize=512;
+};
+
+class CallCommand : public DaemonCommand{
+	public:
+		CallCommand() : DaemonCommand("call", "call <sip address>","Place a call."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			LinphoneCall *call;
+			call=linphone_core_invite(app->getCore(),args);
+			if (call==NULL){
+				app->sendResponse(Response("Call creation failed."));
+			}else{
+				Response resp;
+				ostringstream ostr;
+				app->setCallId(call);
+				ostr<<"Id: "<<Daemon::getCallId(call)<<"\n";
+				resp.setBody(ostr.str().c_str());
+				app->sendResponse(resp);
+			}
+		}
+};
+
+class TerminateCommand : public DaemonCommand{
+	public:
+		TerminateCommand() : DaemonCommand("terminate", "call <call id>","Terminate a call."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			LinphoneCall *call = NULL;
+			int cid;
+			const MSList *elem;
+			if (sscanf(args,"%i",&cid)==1){
+				call=app->findCall(cid);
+				if (call==NULL){
+					app->sendResponse(Response("No call with such id."));
+					return;
+				}
+			}
+			elem=linphone_core_get_calls(app->getCore());
+			if (elem!=NULL && elem->next==NULL){
+				call=(LinphoneCall*)elem->data;
+			}
+			if (call==NULL){
+				app->sendResponse(Response("No active call."));
+				return;
+			}
+			linphone_core_terminate_call(app->getCore(),call);
+			app->sendResponse(Response());
+		}
+};
+
+class QuitCommand : public DaemonCommand{
+	public:
+		QuitCommand() : DaemonCommand("quit", "quit","Quit the application."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			app->quit();
+			app->sendResponse(Response());
+		}
+};
+
+class HelpCommand : public DaemonCommand{
+	public:
+		HelpCommand() : DaemonCommand("help", "help","Show available commands."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			char str[4096]={0};
+			int written=0;
+			list<DaemonCommand*>::const_iterator it;
+			const list<DaemonCommand*> &l=app->getCommandList();
+			for(it=l.begin();it!=l.end();++it){
+				written+=snprintf(str+written,sizeof(str)-written,"%s\t%s\n",(*it)->getProto().c_str(),(*it)->getHelp().c_str());
+			}
+			Response resp;
+			resp.setBody(str);
+			app->sendResponse(resp);
+		}
+};
+
+class PopEventCommand :public DaemonCommand{
+	public:
+		PopEventCommand() : DaemonCommand("pop-event", "pop-event","Pop an event from event queue and display it."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			app->pullEvent();
+		}
+};
+
+class AnswerCommand :public DaemonCommand{
+	public:
+		AnswerCommand() : DaemonCommand("answer", "answer","Answer an incoming call."){
+		}
+		virtual void exec(Daemon *app, const char *args){
+			LinphoneCore *lc=app->getCore();
+			const MSList* elem=linphone_core_get_calls(lc);
+			for(;elem!=NULL;elem=elem->next){
+				LinphoneCall *call=(LinphoneCall*)elem->data;
+				LinphoneCallState cstate=linphone_call_get_state(call);
+				if (cstate==LinphoneCallIncomingReceived || cstate==LinphoneCallIncomingEarlyMedia){
+					if (linphone_core_accept_call(lc,call)==0){
+						app->sendResponse(Response());
+						return;
+					}
+				}
+			}
+			app->sendResponse(Response("No call to accept."));
+		}
+};
+
+EventResponse::EventResponse(LinphoneCall *call, LinphoneCallState state){
+	ostringstream ostr;
+	char *remote=linphone_call_get_remote_address_as_string(call);
+	ostr<<"Event-type: call-state-changed\nEvent: "<<linphone_call_state_to_string(state)<<"\n";
+	ostr<<"From: "<<remote<<"\n";
+	ostr<<"Id: "<<Daemon::getCallId(call)<<"\n";
+	setBody(ostr.str().c_str());
+	ms_free(remote);
+}
+
+DaemonCommand::DaemonCommand(const char *name, const char *proto, const char *help) : mName(name), mProto(proto), mHelp(help){
+}
+
+bool DaemonCommand::matches(const char *name)const{
+	return strcmp(name,mName.c_str())==0;
+}
+
+Daemon * Daemon::sZis=NULL;
+int Daemon::sCallIds=0;
+
+Daemon::Daemon(const char *config_path, bool using_pipes){
+	sZis=this;
+	mServerFd=-1;
+	mChildFd=-1;
+	if (!using_pipes){
+		initReadline();	
+	}else{
+		mServerFd=ortp_server_pipe_create("ha-linphone");
+		listen(mServerFd,2);
+		fprintf(stdout,"Server unix socket created, fd=%i\n",mServerFd);
+	}
+	LinphoneCoreVTable vtable={0};
+	vtable.call_state_changed=callStateChanged;
+	mLc=linphone_core_new(&vtable,NULL,config_path,this);
+	linphone_core_enable_video(mLc,false,false);
+	linphone_core_set_playback_device(mLc,"HEADACOUSTICS");
+	linphone_core_set_ringer_device(mLc,"HEADACOUSTICS");
+	linphone_core_set_capture_device(mLc,"HEADACOUSTICS");
+	linphone_core_enable_echo_cancellation(mLc,false);
+	initCommands();
+}
+
+const list<DaemonCommand*> &Daemon::getCommandList()const{
+	return mCommands;
+}
+
+LinphoneCore * Daemon::getCore(){
+	return mLc;
+}
+
+void Daemon::setCallId(LinphoneCall *call){
+	linphone_call_set_user_pointer(call,(void*)(long)++sCallIds);
+}
+
+LinphoneCall * Daemon::findCall(int id){
+	const MSList *elem=linphone_core_get_calls(mLc);
+	for (;elem!=NULL;elem=elem->next){
+		LinphoneCall *call=(LinphoneCall *)elem->data;
+		if (linphone_call_get_user_pointer(call)==(void*)(long)id)
+			return call;
+	}
+	return NULL;
+}
+
+void Daemon::initCommands(){
+	mCommands.push_back(new CallCommand());
+	mCommands.push_back(new TerminateCommand());
+	mCommands.push_back(new PopEventCommand());
+	mCommands.push_back(new AnswerCommand());
+	mCommands.push_back(new QuitCommand());
+	mCommands.push_back(new HelpCommand());
+	
+}
+
+bool Daemon::pullEvent(){
+	if (!mEventQueue.empty()){
+		Response *r=mEventQueue.front();
+		mEventQueue.pop();
+		sendResponse(*r);
+		delete r;
+		return true;
+	}else{
+		sendResponse(Response());
+	}
+	return false;
+}
+
+int Daemon::getCallId(LinphoneCall *call){
+	return (int)(long)linphone_call_get_user_pointer(call);
+}
+
+void Daemon::callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg){
+	switch(state){
+		case LinphoneCallOutgoingProgress:
+		case LinphoneCallIncomingReceived:
+		case LinphoneCallIncomingEarlyMedia:
+		case LinphoneCallConnected:
+		case LinphoneCallStreamsRunning:
+		case LinphoneCallError:
+		case LinphoneCallEnd:
+			mEventQueue.push(new EventResponse(call,state));
+		break;
+		default:
+		break;
+	}	
+}
+
+void Daemon::callStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state, const char *msg){
+	Daemon *app=(Daemon*)linphone_core_get_user_data(lc);
+	app->callStateChanged(call,state,msg);
+}
+
+void Daemon::iterate(){
+	linphone_core_iterate(mLc);
+	if (mChildFd==-1){
+		if (!mEventQueue.empty()){
+			Response *r=mEventQueue.front();
+			mEventQueue.pop();
+			fprintf(stdout,"%s\n",r->getBody().c_str());
+			fflush(stdout);
+			delete r;
+		}
+	}
+}
+
+int Daemon::readlineHook(){
+	sZis->iterate();
+	return 0;
+}
+
+void Daemon::initReadline()
+{
+	const char *homedir=getenv("HOME");
+	rl_readline_name = "ha";
+
+	rl_set_keyboard_input_timeout(20000);
+	rl_event_hook=readlineHook;
+
+	if (homedir==NULL) homedir=".";
+	mHistfile=string(homedir) + string("/.linphone_history");
+	read_history(mHistfile.c_str());
+        setlinebuf(stdout);
+}
+
+void Daemon::execCommand(const char *cl){
+	char args[sLineSize]={0};
+	char name[sLineSize]={0};
+	sscanf(cl,"%s %s",name,args);
+	list<DaemonCommand*>::iterator it=find_if(mCommands.begin(),mCommands.end(),bind2nd(mem_fun(&DaemonCommand::matches),name));
+	if (it!=mCommands.end()){
+		(*it)->exec(this,args);
+	}else{
+		sendResponse(Response("Unknown command."));
+	}
+}
+
+void Daemon::sendResponse(const Response &resp){
+	char buf[4096]={0};
+	int size;
+	size=resp.toBuf(buf,sizeof(buf));
+	if (mChildFd!=-1){
+		if (write(mChildFd,buf,size)==-1){
+			ms_error("Fail to write to pipe: %s",strerror(errno));
+		}
+	}else{
+		fprintf(stdout,"%s",buf);
+		fflush(stdout);
+	}
+}
+
+char *Daemon::readPipe(char *buffer, int buflen){
+	struct pollfd pfd[2]={{0},{0}};
+	int nfds=1;
+	if (mServerFd!=-1){
+		pfd[0].events=POLLIN;
+		pfd[0].fd=mServerFd;
+	}
+	if (mChildFd!=-1){
+		pfd[1].events=POLLIN;
+		pfd[1].fd=mChildFd;
+		nfds++;
+	}
+	iterate();
+	int err=poll(pfd,nfds,50);
+	if (err>0){
+		if (mServerFd!=-1 && (pfd[0].revents & POLLIN)){
+			struct sockaddr_storage addr;
+			socklen_t addrlen=sizeof(addr);
+			int childfd=accept(mServerFd,(struct sockaddr*)&addr,&addrlen);
+			if (childfd!=-1){
+				if (mChildFd!=-1){
+					ms_error("Cannot accept two client at the same time");
+					close(childfd);
+				}else{
+					mChildFd=childfd;
+					return NULL;
+				}
+			}
+		}
+		if (mChildFd!=-1 && (pfd[1].revents & POLLIN)){
+			int ret;
+			if ((ret=read(mChildFd,buffer,buflen))==-1){
+				ms_error("Fail to read from pipe: %s", strerror(errno));
+			}else{
+				if (ret==0){
+					ms_message("Client disconnected");
+					close(mChildFd);
+					mChildFd=-1;
+					return NULL;
+				}
+				buffer[ret]=0;
+				return buffer;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void printHelp(){
+	fprintf(stdout,"ha-linphone [<options>]\n"
+			"where options are :\n"
+			"\t--help\t\tPrint this notice.\n"
+			"\t--pipe\t\tCreate an unix server socket to receive commands.\n"
+			"\t--config <path>\tSupply a linphonerc style config file to start with.\n");
+}
+
+int Daemon::run(){
+	char line[sLineSize]="ha-linphone>";
+	char *ret;
+	mRunning=true;
+	while(mRunning){
+		if (mServerFd==-1){
+			ret=readline(line);
+			if (ret && ret[0]!='\0') {
+				add_history(ret);
+			}
+		}else{
+			ret=readPipe(line,sLineSize);	
+		}
+		if (ret && ret[0]!='\0') {
+			execCommand(ret);
+		}		
+	}
+	return 0;
+}
+
+void Daemon::quit(){
+	mRunning=false;
+}
+
+Daemon::~Daemon(){
+	if (mChildFd!=-1){
+		close(mChildFd);
+	}
+	if (mServerFd!=-1){
+		ortp_server_pipe_close(mServerFd);
+	}
+	stifle_history(30);
+	write_history(mHistfile.c_str());
+}
+
+int main(int argc, char *argv[]){
+	const char *config_path=NULL;
+	bool using_pipes=false;
+	int i;
+
+	for(i=1;i<argc;++i){
+		if (strcmp(argv[i],"--help")==0){
+			printHelp();
+			return 0;
+		}else if (strcmp(argv[i],"--pipe")==0){
+			using_pipes=true;		
+		}else if (strcmp(argv[i],"--config")==0){
+			config_path=argv[i+1];
+		}
+	}
+	Daemon app(config_path,using_pipes);
+	return app.run();
+};
+
+