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