/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. *//* * * ASTerisk MANager * */#include"asterisk.h"ASTERISK_FILE_VERSION(__FILE__,"$Revision$")#include"asterisk.h"#include<newt.h>#include<stdio.h>#include<sys/time.h>#include<netdb.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/socket.h>#include<sys/select.h>#include<fcntl.h>#include<string.h>#include<errno.h>#include<unistd.h>#include<stdlib.h>#include"asterisk/md5.h"#include"asterisk/linkedlists.h"#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))#undef gethostbyname#define MAX_HEADERS 80#define MAX_LEN 256/* * 2005.05.27 - different versions of newt define the type of the buffer * for the 5th argument to newtEntry() as char ** or const char ** . To * let the code compile cleanly with -Werror, we cast it to void * through * _NEWT_CAST. */#define _NEWT_CAST (void *)#define DEFAULT_MANAGER_PORT 5038structmessage{unsignedinthdrcount;charheaders[MAX_HEADERS][MAX_LEN];};staticstructast_mansession{structsockaddr_insin;intfd;charinbuf[MAX_LEN];intinlen;}session;structast_chan{charname[80];charexten[20];charcontext[20];charpriority[20];charcallerid[40];charstate[10];AST_LIST_ENTRY(ast_chan)list;};staticAST_LIST_HEAD_NOLOCK_STATIC(chans,ast_chan);/* dummy functions to be compatible with the Asterisk core for md5.c */voidast_register_file_version(constchar*file,constchar*version);voidast_register_file_version(constchar*file,constchar*version){}voidast_unregister_file_version(constchar*file);voidast_unregister_file_version(constchar*file){}#if !defined(LOW_MEMORY)intast_add_profile(constchar*,uint64_tscale);intast_add_profile(constchar*s,uint64_tscale){return-1;}int64_tast_profile(int,int64_t);int64_tast_profile(intkey,int64_tval){return0;}int64_tast_mark(int,intstart1_stop0);int64_tast_mark(intkey,intstart1_stop0){return0;}#endif /* LOW_MEMORY *//* end of dummy functions */staticstructast_chan*find_chan(char*name){structast_chan*chan;AST_LIST_TRAVERSE(&chans,chan,list){if(!strcmp(name,chan->name))returnchan;}chan=malloc(sizeof(structast_chan));if(chan){memset(chan,0,sizeof(structast_chan));strncpy(chan->name,name,sizeof(chan->name)-1);AST_LIST_INSERT_TAIL(&chans,chan,list);}returnchan;}staticvoiddel_chan(char*name){structast_chan*chan;AST_LIST_TRAVERSE_SAFE_BEGIN(&chans,chan,list){if(!strcmp(name,chan->name)){AST_LIST_REMOVE_CURRENT(list);free(chan);return;}}AST_LIST_TRAVERSE_SAFE_END;}staticvoid__attribute__((format(printf,2,3)))fdprintf(intfd,char*fmt,...){charstuff[4096];va_listap;intres;va_start(ap,fmt);vsnprintf(stuff,sizeof(stuff),fmt,ap);va_end(ap);if((res=write(fd,stuff,strlen(stuff)))<0){fprintf(stderr,"write() failed: %s\n",strerror(errno));}}staticchar*get_header(structmessage*m,char*var){charcmp[80];intx;snprintf(cmp,sizeof(cmp),"%s: ",var);for(x=0;x<m->hdrcount;x++)if(!strncasecmp(cmp,m->headers[x],strlen(cmp)))returnm->headers[x]+strlen(cmp);return"";}staticintevent_newstate(structast_mansession*s,structmessage*m){structast_chan*chan;chan=find_chan(get_header(m,"Channel"));strncpy(chan->state,get_header(m,"State"),sizeof(chan->state)-1);return0;}staticintevent_newexten(structast_mansession*s,structmessage*m){structast_chan*chan;chan=find_chan(get_header(m,"Channel"));strncpy(chan->exten,get_header(m,"Extension"),sizeof(chan->exten)-1);strncpy(chan->context,get_header(m,"Context"),sizeof(chan->context)-1);strncpy(chan->priority,get_header(m,"Priority"),sizeof(chan->priority)-1);return0;}staticintevent_newchannel(structast_mansession*s,structmessage*m){structast_chan*chan;chan=find_chan(get_header(m,"Channel"));strncpy(chan->state,get_header(m,"State"),sizeof(chan->state)-1);strncpy(chan->callerid,get_header(m,"Callerid"),sizeof(chan->callerid)-1);return0;}staticintevent_status(structast_mansession*s,structmessage*m){structast_chan*chan;chan=find_chan(get_header(m,"Channel"));strncpy(chan->state,get_header(m,"State"),sizeof(chan->state)-1);strncpy(chan->callerid,get_header(m,"Callerid"),sizeof(chan->callerid)-1);strncpy(chan->exten,get_header(m,"Extension"),sizeof(chan->exten)-1);strncpy(chan->context,get_header(m,"Context"),sizeof(chan->context)-1);strncpy(chan->priority,get_header(m,"Priority"),sizeof(chan->priority)-1);return0;}staticintevent_hangup(structast_mansession*s,structmessage*m){del_chan(get_header(m,"Channel"));return0;}staticintevent_ignore(structast_mansession*s,structmessage*m){return0;}staticintevent_rename(structast_mansession*s,structmessage*m){structast_chan*chan;chan=find_chan(get_header(m,"Oldname"));strncpy(chan->name,get_header(m,"Newname"),sizeof(chan->name)-1);return0;}staticstructevent{char*event;int(*func)(structast_mansession*s,structmessage*m);}events[]={{"Newstate",event_newstate},{"Newchannel",event_newchannel},{"Newexten",event_newexten},{"Hangup",event_hangup},{"Rename",event_rename},{"Status",event_status},{"Link",event_ignore},{"Unlink",event_ignore},{"StatusComplete",event_ignore},{"Dial",event_ignore},{"PeerStatus",event_ignore},{"MessageWaiting",event_ignore},{"Newcallerid",event_ignore},{"AGIExec",event_ignore},{"VarSet",event_ignore},{"MeetmeTalking",event_ignore},{"MeetmeJoin",event_ignore},{"MeetmeLeave",event_ignore},{"MeetmeEnd",event_ignore},{"MeetmeMute",event_ignore},{"Masquerade",event_ignore},};staticintprocess_message(structast_mansession*s,structmessage*m){intx;charevent[80]="";strncpy(event,get_header(m,"Event"),sizeof(event)-1);if(!strlen(event)){fprintf(stderr,"Missing event in request");return0;}for(x=0;x<ARRAY_LEN(events);x++){if(!strcasecmp(event,events[x].event)){if(events[x].func(s,m))return-1;break;}}if(x>=ARRAY_LEN(events))fprintf(stderr,"Ignoring unknown event '%s'",event);#if 0 for (x=0;x<m->hdrcount;x++) { printf("Header: %s\n", m->headers[x]); }#endif return0;}staticvoidrebuild_channels(newtComponentc){void*prev=NULL;structast_chan*chan;chartmpn[42];chartmp[256];intx=0;prev=newtListboxGetCurrent(c);newtListboxClear(c);AST_LIST_TRAVERSE(&chans,chan,list){snprintf(tmpn,sizeof(tmpn),"%s (%s)",chan->name,chan->callerid);if(strlen(chan->exten))snprintf(tmp,sizeof(tmp),"%-30s %8s -> %s@%s:%s",tmpn,chan->state,chan->exten,chan->context,chan->priority);elsesnprintf(tmp,sizeof(tmp),"%-30s %8s",tmpn,chan->state);newtListboxAppendEntry(c,tmp,chan);x++;}if(!x)newtListboxAppendEntry(c," << No Active Channels >> ",NULL);newtListboxSetCurrentByKey(c,prev);}staticinthas_input(structast_mansession*s){intx;for(x=1;x<s->inlen;x++)if((s->inbuf[x]=='\n')&&(s->inbuf[x-1]=='\r'))return1;return0;}staticintget_input(structast_mansession*s,char*output){/* output must have at least sizeof(s->inbuf) space */intres;intx;structtimevaltv={0,0};fd_setfds;for(x=1;x<s->inlen;x++){if((s->inbuf[x]=='\n')&&(s->inbuf[x-1]=='\r')){/* Copy output data up to and including \r\n */memcpy(output,s->inbuf,x+1);/* Add trailing \0 */output[x+1]='\0';/* Move remaining data back to the front */memmove(s->inbuf,s->inbuf+x+1,s->inlen-x);s->inlen-=(x+1);return1;}}if(s->inlen>=sizeof(s->inbuf)-1){fprintf(stderr,"Dumping long line with no return from %s: %s\n",inet_ntoa(s->sin.sin_addr),s->inbuf);s->inlen=0;}FD_ZERO(&fds);FD_SET(s->fd,&fds);res=select(s->fd+1,&fds,NULL,NULL,&tv);if(res<0){fprintf(stderr,"Select returned error: %s\n",strerror(errno));}elseif(res>0){res=read(s->fd,s->inbuf+s->inlen,sizeof(s->inbuf)-1-s->inlen);if(res<1)return-1;s->inlen+=res;s->inbuf[s->inlen]='\0';}else{return2;}return0;}staticintinput_check(structast_mansession*s,structmessage**mout){staticstructmessagem;intres;if(mout)*mout=NULL;for(;;){res=get_input(s,m.headers[m.hdrcount]);if(res==1){#if 0 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]); fgetc(stdin);#endif/* Strip trailing \r\n */if(strlen(m.headers[m.hdrcount])<2)continue;m.headers[m.hdrcount][strlen(m.headers[m.hdrcount])-2]='\0';if(!strlen(m.headers[m.hdrcount])){if(mout&&strlen(get_header(&m,"Response"))){*mout=&m;return0;}if(process_message(s,&m))break;memset(&m,0,sizeof(&m));}elseif(m.hdrcount<MAX_HEADERS-1)m.hdrcount++;}elseif(res<0){return-1;}elseif(res==2)return0;}return-1;}staticstructmessage*wait_for_response(inttimeout){structmessage*m;structtimevaltv;intres;fd_setfds;for(;;){tv.tv_sec=timeout/1000;tv.tv_usec=(timeout%1000)*1000;FD_SET(session.fd,&fds);res=select(session.fd+1,&fds,NULL,NULL,&tv);if(res<1)break;if(input_check(&session,&m)<0){returnNULL;}if(m)returnm;}returnNULL;}staticint__attribute__((format(printf,2,3)))manager_action(char*action,char*fmt,...){structast_mansession*s;chartmp[4096];va_listap;intres;s=&session;fdprintf(s->fd,"Action: %s\r\n",action);va_start(ap,fmt);vsnprintf(tmp,sizeof(tmp),fmt,ap);va_end(ap);if((res=write(s->fd,tmp,strlen(tmp)))<0){fprintf(stderr,"write() failed: %s\n",strerror(errno));}fdprintf(s->fd,"\r\n");return0;}staticintshow_message(char*title,char*msg){newtComponentform;newtComponentlabel;newtComponentok;structnewtExitStructes;newtCenteredWindow(60,7,title);label=newtLabel(4,1,msg);ok=newtButton(27,3,"OK");form=newtForm(NULL,NULL,0);newtFormAddComponents(form,label,ok,NULL);newtFormRun(form,&es);newtPopWindow();newtFormDestroy(form);return0;}staticnewtComponentshowform;staticintshow_doing(char*title,char*tmp){structnewtExitStructes;newtComponentlabel;showform=newtForm(NULL,NULL,0);newtCenteredWindow(70,4,title);label=newtLabel(3,1,tmp);newtFormAddComponents(showform,label,NULL);newtFormSetTimer(showform,200);newtFormRun(showform,&es);return0;}staticinthide_doing(void){newtPopWindow();newtFormDestroy(showform);return0;}staticvoidtry_status(void){structmessage*m;manager_action("Status","%s","");m=wait_for_response(10000);if(!m){show_message("Status Failed","Timeout waiting for response");}elseif(strcasecmp(get_header(m,"Response"),"Success")){show_message("Status Failed Failed",get_header(m,"Message"));}}staticvoidtry_hangup(newtComponentc){structast_chan*chan;structmessage*m;chan=newtListboxGetCurrent(c);if(chan){manager_action("Hangup","Channel: %s\r\n",chan->name);m=wait_for_response(10000);if(!m){show_message("Hangup Failed","Timeout waiting for response");}elseif(strcasecmp(get_header(m,"Response"),"Success")){show_message("Hangup Failed",get_header(m,"Message"));}}}staticintget_user_input(char*msg,char*buf,intbuflen){newtComponentform;newtComponentok;newtComponentcancel;newtComponentinpfield;constchar*input;intres=-1;structnewtExitStructes;newtCenteredWindow(60,7,msg);inpfield=newtEntry(5,2,"",50,_NEWT_CAST&input,0);ok=newtButton(22,3,"OK");cancel=newtButton(32,3,"Cancel");form=newtForm(NULL,NULL,0);newtFormAddComponents(form,inpfield,ok,cancel,NULL);newtFormRun(form,&es);strncpy(buf,input,buflen-1);if(es.u.co==ok)res=0;elseres=-1;newtPopWindow();newtFormDestroy(form);returnres;}staticvoidtry_redirect(newtComponentc){structast_chan*chan;chardest[256];structmessage*m;charchanname[256];chartmp[80];char*context;chan=newtListboxGetCurrent(c);if(chan){strncpy(channame,chan->name,sizeof(channame)-1);snprintf(tmp,sizeof(tmp),"Enter new extension for %s",channame);if(get_user_input(tmp,dest,sizeof(dest)))return;if((context=strchr(dest,'@'))){*context='\0';context++;manager_action("Redirect","Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n",chan->name,context,dest);}else{manager_action("Redirect","Channel: %s\r\nExten: %s\r\nPriority: 1\r\n",chan->name,dest);}m=wait_for_response(10000);if(!m){show_message("Hangup Failed","Timeout waiting for response");}elseif(strcasecmp(get_header(m,"Response"),"Success")){show_message("Hangup Failed",get_header(m,"Message"));}}}staticintmanage_calls(char*host){newtComponentform;newtComponentquit;newtComponenthangup;newtComponentredirect;newtComponentchannels;structnewtExitStructes;chartmp[80];/* Mark: If there's one thing you learn from this code, it is this... Never, ever fly Air France. Their customer service is absolutely the worst. I've never heard the words "That's not my problem" as many times as I have from their staff -- It should, without doubt be their corporate motto if it isn't already. Don't bother giving them business because you're just a pain in their side and they will be sure to let you know the first time you speak to them. If you ever want to make me happy just tell me that you, too, will never fly Air France again either (in spite of their excellent cuisine). Update by oej: The merger with KLM has transferred this behaviour to KLM as well. Don't bother giving them business either... Only if you want to travel randomly without luggage, you might pick either of them. */snprintf(tmp,sizeof(tmp),"Asterisk Manager at %s",host);newtCenteredWindow(74,20,tmp);form=newtForm(NULL,NULL,0);newtFormWatchFd(form,session.fd,NEWT_FD_READ);newtFormSetTimer(form,100);quit=newtButton(62,16,"Quit");redirect=newtButton(35,16,"Redirect");hangup=newtButton(50,16,"Hangup");channels=newtListbox(1,1,14,NEWT_FLAG_SCROLL);newtFormAddComponents(form,channels,redirect,hangup,quit,NULL);newtListboxSetWidth(channels,72);show_doing("Getting Status","Retrieving system status...");try_status();hide_doing();for(;;){newtFormRun(form,&es);if(has_input(&session)||(es.reason==NEWT_EXIT_FDREADY)){if(input_check(&session,NULL)){show_message("Disconnected","Disconnected from remote host");break;}}elseif(es.reason==NEWT_EXIT_COMPONENT){if(es.u.co==quit)break;if(es.u.co==hangup){try_hangup(channels);}elseif(es.u.co==redirect){try_redirect(channels);}}rebuild_channels(channels);}newtFormDestroy(form);return0;}staticintmanager_login(char*hostname){newtComponentform;newtComponentcancel;newtComponentlogin;newtComponentusername;newtComponentpassword;newtComponentlabel;newtComponentulabel;newtComponentplabel;constchar*user;constchar*pass;structmessage*m;structnewtExitStructes;chartmp[55];structhostent*hp;intres=-1;session.fd=socket(AF_INET,SOCK_STREAM,0);if(session.fd<0){snprintf(tmp,sizeof(tmp),"socket() failed: %s\n",strerror(errno));show_message("Socket failed",tmp);return-1;}snprintf(tmp,sizeof(tmp),"Looking up %s\n",hostname);show_doing("Connecting....",tmp);hp=gethostbyname(hostname);if(!hp){snprintf(tmp,sizeof(tmp),"No such address: %s\n",hostname);show_message("Host lookup failed",tmp);return-1;}hide_doing();snprintf(tmp,sizeof(tmp),"Connecting to %s",hostname);show_doing("Connecting...",tmp);session.sin.sin_family=AF_INET;session.sin.sin_port=htons(DEFAULT_MANAGER_PORT);memcpy(&session.sin.sin_addr,hp->h_addr,sizeof(session.sin.sin_addr));if(connect(session.fd,(structsockaddr*)&session.sin,sizeof(session.sin))){snprintf(tmp,sizeof(tmp),"%s failed: %s\n",hostname,strerror(errno));show_message("Connect Failed",tmp);return-1;}hide_doing();login=newtButton(5,6,"Login");cancel=newtButton(25,6,"Cancel");newtCenteredWindow(40,10,"Asterisk Manager Login");snprintf(tmp,sizeof(tmp),"Host: %s",hostname);label=newtLabel(4,1,tmp);ulabel=newtLabel(4,2,"Username:");plabel=newtLabel(4,3,"Password:");username=newtEntry(14,2,"",20,_NEWT_CAST&user,0);password=newtEntry(14,3,"",20,_NEWT_CAST&pass,NEWT_FLAG_HIDDEN);form=newtForm(NULL,NULL,0);newtFormAddComponents(form,username,password,login,cancel,label,ulabel,plabel,NULL);newtFormRun(form,&es);if(es.reason==NEWT_EXIT_COMPONENT){if(es.u.co==login){snprintf(tmp,sizeof(tmp),"Logging in '%s'...",user);show_doing("Logging in",tmp);/* Check to see if the remote host supports MD5 Authentication */manager_action("Challenge","AuthType: MD5\r\n");m=wait_for_response(10000);if(m&&!strcasecmp(get_header(m,"Response"),"Success")){char*challenge=get_header(m,"Challenge");intx;intlen=0;charmd5key[256]="";structMD5Contextmd5;unsignedchardigest[16];MD5Init(&md5);MD5Update(&md5,(unsignedchar*)challenge,strlen(challenge));MD5Update(&md5,(unsignedchar*)pass,strlen(pass));MD5Final(digest,&md5);for(x=0;x<16;x++)len+=sprintf(md5key+len,"%2.2x",digest[x]);manager_action("Login","AuthType: MD5\r\n""Username: %s\r\n""Key: %s\r\n",user,md5key);m=wait_for_response(10000);hide_doing();if(!strcasecmp(get_header(m,"Response"),"Success")){res=0;}else{show_message("Login Failed",get_header(m,"Message"));}}else{memset(m,0,sizeof(m));manager_action("Login","Username: %s\r\n""Secret: %s\r\n",user,pass);m=wait_for_response(10000);hide_doing();if(m){if(!strcasecmp(get_header(m,"Response"),"Success")){res=0;}else{show_message("Login Failed",get_header(m,"Message"));}}}}}newtFormDestroy(form);returnres;}intmain(intargc,char*argv[]){if(argc<2){fprintf(stderr,"Usage: astman <host>\n");exit(1);}newtInit();newtCls();newtDrawRootText(0,0,"Asterisk Manager (C)2002, Linux Support Services, Inc.");newtPushHelpLine("Welcome to the Asterisk Manager!");if(manager_login(argv[1])){newtFinished();exit(1);}manage_calls(argv[1]);newtFinished();return0;}