1 /****************************************************************************
2  *  Copyright (C) 2009  Simon MORLAT <simon.morlat@linphone.org>
3  *
4  ****************************************************************************
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  ****************************************************************************/
21 
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 
28 #ifdef _WIN32
29 #include <ws2tcpip.h>
30 #include <windows.h>
31 #include <winbase.h>
32 #include <ctype.h>
33 #include <conio.h>
34 #else
35 #include <sys/socket.h>
36 #include <netdb.h>
37 #include <sys/un.h>
38 
39 #endif
40 
41 #include "ortp/ortp.h"
42 #include <bctoolbox/port.h>
43 
44 #define DEFAULT_REPLY_SIZE 4096
45 
46 #define STATUS_REGISTERED (1<<0)
47 #define STATUS_REGISTERING (1<<1)
48 #define STATUS_DIALING (1<<2)
49 #define STATUS_AUTOANSWER (1<<3)
50 #define STATUS_IN_CONNECTED (1<<4) /* incoming call accepted */
51 #define STATUS_OUT_CONNECTED (1<<5) /*outgoing call accepted */
52 #define STATUS_IN_COMING (1<<6) /*incoming call pending */
53 
54 
make_status_value(const char * status_string)55 static int make_status_value(const char *status_string){
56 	int ret=0;
57 	if (strstr(status_string,"registered, identity=")){
58 		ret|=STATUS_REGISTERED;
59 	}
60 	if (strstr(status_string,"registered=-1")){
61 		ret|=STATUS_REGISTERING;
62 	}
63 	if (strstr(status_string,"autoanswer=1")){
64 		ret|=STATUS_AUTOANSWER;
65 	}
66 	if (strstr(status_string,"dialing")){
67 		ret|=STATUS_DIALING;
68 	}
69 	if (strstr(status_string,"Call out")){
70 		ret|=STATUS_OUT_CONNECTED;
71 	}
72 	if (strstr(status_string,"hook=answered")){
73 		ret|=STATUS_IN_CONNECTED;
74 	}
75 	if (strstr(status_string,"Incoming call from ")){
76 		ret|=STATUS_IN_COMING;
77 	}
78 	return ret;
79 }
80 
send_command(const char * command,char * reply,int reply_len,int print_errors)81 static int send_command(const char *command, char *reply, int reply_len, int print_errors){
82 	ortp_pipe_t pp;
83 	int i;
84 	int err;
85 	char path[128];
86 #ifndef _WIN32
87 	snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
88 #else
89 	{
90 		char username[128];
91 		DWORD size=sizeof(username)-1;
92 		GetUserName(username,&size);
93 		snprintf(path,sizeof(path)-1,"linphonec-%s",username);
94 	}
95 #endif
96 	if ((pp=ortp_client_pipe_connect(path))==ORTP_PIPE_INVALID){
97 		if (print_errors) fprintf(stderr,"ERROR: Failed to connect pipe: %s\n",strerror(errno));
98 		return -1;
99 	}
100 	if (ortp_pipe_write(pp,(uint8_t*)command,strlen(command))==-1){
101 		if (print_errors) fprintf(stderr,"ERROR: Fail to send command to remote linphonec\n");
102 		ortp_client_pipe_close(pp);
103 		return -1;
104 	}
105 	/*wait for replies */
106 	i=0;
107 	while ((err=ortp_pipe_read(pp,(uint8_t*)&reply[i],reply_len-i-1))>0){
108 		i+=err;
109 	}
110 	reply[i]='\0';
111 	ortp_client_pipe_close(pp);
112 	return 0;
113 }
114 
print_usage(void)115 static void print_usage(void){
116 	fprintf(stderr,"Usage:\nlinphonecsh <action> [arguments]\n"
117 			"where action is one of\n"
118 			"\tinit\t\t: spawn a linphonec daemon (first step to make other actions)\n"
119 			"\t\t\tfollowed by the arguments sent to linphonec\n"
120 			"\tgeneric\t\t: sends a generic command to the running linphonec daemon\n"
121 			"\t\t\tfollowed by the generic command surrounded by quotes,\n\t\t\t for example \"call sip:joe@example.net\"\n"
122 			"\tregister\t: register; arguments are \n\t\t\t--host <host>\n\t\t\t--username <username>\n\t\t\t--password <password>\n"
123 			"\tunregister\t: unregister\n"
124 			"\tdial\t\t: dial <sip uri or number>\n"
125 			"\tstatus\t\t: can be 'status register', 'status autoanswer' or 'status hook'\n"
126 			"\tsoundcard\t: can be 'soundcard capture', 'soundcard playback', 'soundcard ring',\n"
127 			"\t\t\t followed by an optional number representing the index of the soundcard,\n"
128 			"\t\t\t in which case the soundcard is set instead of just read.\n"
129 			"\texit\t\t: make the linphonec daemon to exit.\n"
130 	);
131 	exit(-1);
132 }
133 
134 #ifdef _WIN32
argv_to_line(int argc,char * argv[])135 static char *argv_to_line(int argc, char *argv[]) {
136 	int i;
137 	int line_length;
138 	char *line;
139 
140 	assert( argc>=0 );
141 
142 	if(argc == 0) return NULL;
143 
144 	line_length = strlen(argv[0]);
145 	for(i=1; i<argc; i++) {
146 		line_length += strlen(argv[i]) + 1;
147 	}
148 
149 	line = ortp_malloc0((line_length +1) * sizeof(char));
150 	strcat(line, argv[0]);
151 	for(i=1; i<argc; i++) {
152 		strcat(line, " ");
153 		strcat(line, argv[i]);
154 	}
155 	return line;
156 }
157 #endif
158 
159 #define MAX_ARGS 10
160 
161 #ifndef _WIN32
spawn_linphonec(int argc,char * argv[])162 static void spawn_linphonec(int argc, char *argv[]){
163 	char * args[MAX_ARGS];
164 	int i,j;
165 	pid_t pid;
166 	j=0;
167 	args[j++]="linphonec";
168 	args[j++]="--pipe";
169 	args[j++]="-c";
170 	args[j++]="/dev/null";
171 	for(i=0;i<argc;++i){
172 		args[j++]=argv[i];
173 	}
174 	args[j++]=NULL;
175 
176 #ifdef __uClinux__
177 	pid = vfork();
178 #else
179 	pid = fork();
180 #endif
181 	if (pid < 0){
182 		fprintf(stderr,"Could not fork\n");
183 		exit(-1);
184 	}
185 	if (pid == 0) {
186 		int fd;
187 		/*we are the new process*/
188 		setsid();
189 
190 		fd = open("/dev/null", O_RDWR);
191 		if (fd==-1){
192 			fprintf(stderr,"Could not open /dev/null\n");
193 			exit(-1);
194 		}
195 		dup2(fd, 0);
196 		dup2(fd, 1);
197 		dup2(fd, 2);
198 		close(fd);
199 
200 		if (execvp("linphonec",args)==-1){
201 			fprintf(stderr,"Fail to spawn linphonec: %s\n",strerror(errno));
202 			exit(-1);
203 		}
204 	}
205 }
206 #else
207 
spawn_linphonec(int argc,char * argv[])208 static void spawn_linphonec(int argc, char *argv[]){
209 	PROCESS_INFORMATION pinfo;
210 	STARTUPINFO si;
211 	BOOL ret;
212 	const char *cmd = "linphoned.exe --pipe -c NUL";
213 	char *args_in_line = argv_to_line(argc, argv);
214 	char *cmd_with_args;
215 
216 	ZeroMemory( &si, sizeof(si) );
217 	si.cb = sizeof(si);
218 	ZeroMemory( &pinfo, sizeof(pinfo) );
219 
220 	if(args_in_line) {
221 		cmd_with_args = ortp_strdup_printf("%s %s", cmd, args_in_line);
222 	} else {
223 		cmd_with_args = ortp_strdup(cmd);
224 	}
225 
226 	ret=CreateProcess(NULL, cmd_with_args,
227 		NULL,
228 		NULL,
229 		FALSE,
230 		0,
231 		NULL,
232 		NULL,
233 		&si,
234 		&pinfo);
235 
236 	if(args_in_line) ortp_free(args_in_line);
237 	ortp_free(cmd_with_args);
238 
239 	if (!ret){
240 		fprintf(stderr,"Spawning of linphoned.exe failed.\n");
241 	}else{
242 		WaitForInputIdle(pinfo.hProcess,1000);
243 	}
244 }
245 
246 #endif
247 
send_generic_command(const char * command,int print_result)248 static int send_generic_command(const char *command, int print_result){
249 	char reply[DEFAULT_REPLY_SIZE];
250 	int err;
251 	err=send_command(command,reply,sizeof(reply),print_result);
252 	if (err==0 && print_result) {
253 		printf("%s",reply);
254 		fflush(stdout);
255 	}
256 	return err;
257 }
258 
register_execute(int argc,char * argv[])259 static int register_execute(int argc, char *argv[]){
260 	char cmd[512];
261 	char *username=NULL;
262 	char *host=NULL;
263 	char *passwd=NULL;
264 	int i;
265 	for(i=0;i<argc;++i){
266 		if (strcmp(argv[i],"--host")==0){
267 			i++;
268 			if (i<argc){
269 				host=argv[i];
270 			}else print_usage();
271 		}else if (strcmp(argv[i],"--username")==0){
272 			i++;
273 			if (i<argc){
274 				username=argv[i];
275 			}else print_usage();
276 		}else if (strcmp(argv[i],"--password")==0){
277 			i++;
278 			if (i<argc){
279 				passwd=argv[i];
280 			}else print_usage();
281 		}else print_usage();
282 	}
283 	if (username==NULL) {
284 		fprintf(stderr,"Missing --username\n");
285 		print_usage();
286 	}
287 	if (host==NULL) {
288 		fprintf(stderr,"Missing --host\n");
289 		print_usage();
290 	}
291 	if (passwd) snprintf(cmd,sizeof(cmd),"register sip:%s@%s sip:%s %s",username,host,host,passwd);
292 	else snprintf(cmd,sizeof(cmd),"register sip:%s@%s sip:%s",username,host,host);
293 	return send_generic_command(cmd,TRUE);
294 }
295 
unregister_execute(int argc,char * argv[])296 static int unregister_execute(int argc, char *argv[]){
297 	return send_generic_command("unregister",FALSE);
298 }
299 
300 
dial_execute(int argc,char * argv[])301 static int dial_execute(int argc, char *argv[]){
302 	char cmd[512];
303 	if (argc==1){
304 		snprintf(cmd,sizeof(cmd),"call %s",argv[0]);
305 		return send_generic_command(cmd,TRUE);
306 	}else{
307 		print_usage();
308 	}
309 	return -1;
310 }
311 
status_execute(int argc,char * argv[])312 static int status_execute(int argc, char *argv[]){
313 	char cmd[512];
314 	char reply[DEFAULT_REPLY_SIZE];
315 	int err;
316 
317 	if (argc==1){
318 		snprintf(cmd,sizeof(cmd),"status %s",argv[0]);
319 		err=send_command(cmd,reply,sizeof(reply),TRUE);
320 		if (err==0) {
321 			printf("%s",reply);
322 			err=make_status_value(reply);
323 		}
324 		return err;
325 	}else{
326 		print_usage();
327 	}
328 	return -1;
329 }
330 
parse_card_index(const char * reply)331 static int parse_card_index(const char *reply){
332 	int index=-1;
333 	reply=strstr(reply,"device #");
334 	if (!reply || sscanf(reply,"device #%i",&index)!=1){
335 		fprintf(stderr,"Error while parsing linphonec daemon output !\n");
336 	}
337 	return index;
338 }
339 
soundcard_execute(int argc,char * argv[])340 static int soundcard_execute(int argc, char *argv[]){
341 	char cmd[512];
342 	char reply[DEFAULT_REPLY_SIZE];
343 	int err;
344 	if (argc==1){
345 		snprintf(cmd,sizeof(cmd),"soundcard %s",argv[0]);
346 		err=send_command(cmd,reply,sizeof(reply),TRUE);
347 		if (err==0) {
348 			printf("%s",reply);
349 			return parse_card_index(reply);
350 		}
351 	}else if (argc==2){/*setting a soundcard */
352 		snprintf(cmd,sizeof(cmd),"soundcard %s %s",argv[0],argv[1]);
353 		err=send_command(cmd,reply,sizeof(reply),TRUE);
354 		if (err==0) {
355 			printf("%s",reply);
356 			return 0;
357 		}
358 	}else{
359 		print_usage();
360 	}
361 	return -1;
362 }
363 
main(int argc,char * argv[])364 int main(int argc, char *argv[]){
365 	int argi;
366 	if (argc<2){
367 		print_usage();
368 		return -1;
369 	}
370 	ortp_init();
371 	for(argi=1;argi<argc;++argi){
372 		if (strcmp(argv[argi],"init")==0){
373 			/*check if there is running instance*/
374 			if (send_generic_command("help",0)==0){
375 				fprintf(stderr,"A running linphonec has been found, not spawning a second one.\n");
376 				return 0;
377 			}
378 			spawn_linphonec(argc-argi-1,&argv[argi+1]);
379 			return 0;
380 		}else if (strcmp(argv[argi],"generic")==0){
381 			if (argi+1<argc){
382 				return send_generic_command(argv[argi+1],1);
383 			}else print_usage();
384 		}else if (strcmp(argv[argi],"register")==0){
385 			return register_execute(argc-argi-1,&argv[argi+1]);
386 		}else if (strcmp(argv[argi],"unregister")==0){
387 			return unregister_execute(argc-argi-1,&argv[argi+1]);
388 		}else if (strcmp(argv[argi],"dial")==0){
389 			return dial_execute(argc-argi-1,&argv[argi+1]);
390 		}else if (strcmp(argv[argi],"hangup")==0){
391 			send_generic_command("terminate",FALSE);
392 			send_generic_command("duration",TRUE);
393 		}else if (strcmp(argv[argi],"status")==0){
394 			return status_execute(argc-argi-1,&argv[argi+1]);
395 		}else if (strcmp(argv[argi],"soundcard")==0){
396 			return soundcard_execute(argc-argi-1,&argv[argi+1]);
397 		}else if (strcmp(argv[argi],"exit")==0){
398 			return send_generic_command("quit",TRUE);
399 		}else print_usage();
400 	}
401   	return 0;
402 }
403