#include #include #include #include #include #include #include #include #include #include #include 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 *msg, Status status = Error) : mStatus(status) { if (status == Ok) { mBody = msg; } else { mReason = msg; } } 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 PayloadTypeResponse: public Response { public: PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index = -1, const string &prefix = string(), bool enabled_status = true); private: }; class Daemon { friend class DaemonCommand; public: typedef Response::Status Status; Daemon(const char *config_path, const char *factory_config_path, const char *log_file, const char *pipe_name, bool display_video, bool capture_video); ~Daemon(); int run(); void quit(); void sendResponse(const Response &resp); LinphoneCore *getCore(); const list &getCommandList() const; LinphoneCall *findCall(int id); LinphoneProxyConfig *findProxy(int id); AudioStream *findAudioStream(int id); bool pullEvent(); static int getCallId(LinphoneCall *call); int setCallId(LinphoneCall *call); static int getProxyId(LinphoneProxyConfig *proxy); int setProxyId(LinphoneProxyConfig *proxy); int setAudioStreamId(AudioStream *audio_stream); 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(); void uninitCommands(); LinphoneCore *mLc; std::list mCommands; queue mEventQueue; int mServerFd; int mChildFd; string mHistfile; bool mRunning; FILE *mLogFile; static Daemon *sZis; static int sCallIds; static int sProxyIds; static int sAudioStreamIds; static const int sLineSize = 512; std::map mAudioStreams; }; class CallCommand: public DaemonCommand { public: CallCommand() : DaemonCommand("call", "call ", "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; ostr << "Id: " << app->setCallId(call) << "\n"; resp.setBody(ostr.str().c_str()); app->sendResponse(resp); } } }; class TerminateCommand: public DaemonCommand { public: TerminateCommand() : DaemonCommand("terminate", "terminate ", "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; } } else { 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::const_iterator it; const list &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 RegisterCommand: public DaemonCommand { public: RegisterCommand() : DaemonCommand("register", "register ", "Register the daemon to a SIP proxy.") { } virtual void exec(Daemon *app, const char *args) { LinphoneCore *lc = app->getCore(); char proxy[256] = { 0 }, identity[128] = { 0 }, password[64] = { 0 }; if (sscanf(args, "%255s %127s %63s", identity, proxy, password) >= 2) { LinphoneProxyConfig *cfg = linphone_proxy_config_new(); if (password[0] != '\0') { LinphoneAddress *from = linphone_address_new(identity); if (from != NULL) { LinphoneAuthInfo *info = linphone_auth_info_new(linphone_address_get_username(from), NULL, password, NULL, NULL); /*create authentication structure from identity*/ linphone_core_add_auth_info(lc, info); /*add authentication info to LinphoneCore*/ linphone_address_destroy(from); linphone_auth_info_destroy(info); } } linphone_proxy_config_set_identity(cfg, identity); linphone_proxy_config_set_server_addr(cfg, proxy); linphone_proxy_config_enable_register(cfg, TRUE); app->setProxyId(cfg); ostringstream ostr; ostr << "Id: " << Daemon::getProxyId(cfg) << "\n"; linphone_core_add_proxy_config(lc, cfg); app->sendResponse(Response(ostr.str().c_str(), Response::Ok)); } else { app->sendResponse(Response("Missing/Incorrect parameter(s).")); } } }; class UnregisterCommand: public DaemonCommand { public: UnregisterCommand() : DaemonCommand("unregister", "unregister ", "Unregister the daemon from proxy.") { } virtual void exec(Daemon *app, const char *args) { LinphoneCore *lc = app->getCore(); LinphoneProxyConfig *cfg = NULL; int pid; if (sscanf(args, "%i", &pid) == 1) { cfg = app->findProxy(pid); if (cfg == NULL) { app->sendResponse(Response("No register with such id.")); return; } } else { app->sendResponse(Response("Missing/Incorrect parameter(s).")); return; } linphone_core_remove_proxy_config(lc, cfg); app->sendResponse(Response()); } }; 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 AudioStreamStartCommand: public DaemonCommand { public: AudioStreamStartCommand() : DaemonCommand("audio-stream-start", "audio-stream-start ", "Start an audio stream.") { } virtual void exec(Daemon *app, const char *args) { char addr[256]; int port; int payload_type; if (sscanf(args, "%255s %d %d", addr, &port, &payload_type) == 3) { int local_port = linphone_core_get_audio_port(app->getCore()); const int jitt = linphone_core_get_audio_jittcomp(app->getCore()); const bool_t echo_canceller = linphone_core_echo_cancellation_enabled(app->getCore()); MSSndCardManager *manager = ms_snd_card_manager_get(); MSSndCard *capture_card = ms_snd_card_manager_get_card(manager, linphone_core_get_capture_device(app->getCore())); MSSndCard *play_card = ms_snd_card_manager_get_card(manager, linphone_core_get_playback_device(app->getCore())); AudioStream *stream = audio_stream_new(local_port, false); if (audio_stream_start_now(stream, &av_profile, addr, port, port + 1, payload_type, jitt, play_card, capture_card, echo_canceller) != 0) { app->sendResponse(Response("Error during audio stream creation.")); } ostringstream ostr; ostr << "Id: " << app->setAudioStreamId(stream) << "\n"; app->sendResponse(Response(ostr.str().c_str(), Response::Ok)); } else { app->sendResponse(Response("Missing/Incorrect parameter(s).")); } } }; class AudioStreamStopCommand: public DaemonCommand { public: AudioStreamStopCommand() : DaemonCommand("audio-stream-stop", "audio-stream-stop