/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This source code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ package local.ua; import org.zoolu.sip.address.*; import org.zoolu.sip.provider.*; import org.zoolu.net.SocketAddress; import org.zoolu.tools.Archive; import org.zoolu.tools.Log; import org.zoolu.tools.LogLevel; import java.io.BufferedReader; import java.io.InputStreamReader; import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.plaf.basic.BasicComboBoxEditor; import java.util.Vector; /** Simple GUI-based SIP user agent (UA). */ public class GraphicalUA extends JFrame implements UserAgentListener, RegisterAgentListener { /** This application */ final String app_name="MjSip mini UA"; /** Event logger. */ Log log; /** User Agent */ UserAgent ua; /** Register Agent */ RegisterAgent ra; /** UserAgentProfile */ UserAgentProfile user_profile; /** Title */ //String user_name=app_name; /** Recent contacts */ protected static final int NMAX_CONTACTS=10; /** Recent contacts */ StringList contact_list; private static final int W_Width=320; // window width private static final int W_Height=90; // window height private static final int C_Height=30; // buttons and combobox height (total) /** Media file path */ final String MEDIA_PATH="media/local/ua/"; final String CALL_GIF=MEDIA_PATH+"call.gif"; final String HANGUP_GIF=MEDIA_PATH+"hangup.gif"; Icon icon_call; Icon icon_hangup; //Icon icon_call=new ImageIcon("media/ua/call.gif"); //Icon icon_hangup=new ImageIcon("media/ua/hangup.gif"); JPanel jPanel1 = new JPanel(); JPanel jPanel2 = new JPanel(); JPanel jPanel3 = new JPanel(); JPanel jPanel4 = new JPanel(); JComboBox jComboBox1 = new JComboBox(); BorderLayout borderLayout1 = new BorderLayout(); BorderLayout borderLayout2 = new BorderLayout(); JPanel jPanel5 = new JPanel(); GridLayout gridLayout2 = new GridLayout(); GridLayout gridLayout3 = new GridLayout(); JButton jButton1 = new JButton(); JButton jButton2 = new JButton(); ComboBoxEditor comboBoxEditor1=new BasicComboBoxEditor(); BorderLayout borderLayout3 = new BorderLayout(); JTextField display=new JTextField(); /** Creates a new GraphicalUA */ public GraphicalUA(SipProvider sip_provider, UserAgentProfile user_profile) { log=sip_provider.getLog(); this.user_profile=user_profile; ua=new UserAgent(sip_provider,user_profile,this); ua.listen(); ra=new RegisterAgent(sip_provider,user_profile.from_url,user_profile.contact_url,user_profile.username,user_profile.realm,user_profile.passwd,this); String jar_file=user_profile.ua_jar; icon_call=Archive.getImageIcon(Archive.getJarURL(jar_file,CALL_GIF)); icon_hangup=Archive.getImageIcon(Archive.getJarURL(jar_file,HANGUP_GIF)); //user_name=user_profile.contact_url; contact_list=new StringList(user_profile.contacts_file); jComboBox1=new JComboBox(contact_list.getElements()); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } //Image image=Archive.getImage(Archive.getJarURL(jar_file,"media/local/ua/intro.gif")); //PopupFrame about=new PopupFrame("About",image,this); //try { Thread.sleep(3000); } catch(Exception e) { } //about.closeWindow(); run(); } private void jbInit() throws Exception { // set frame dimensions this.setSize(W_Width,W_Height); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension frameSize = this.getSize(); if (frameSize.height > screenSize.height) frameSize.height = screenSize.height; if (frameSize.width > screenSize.width) frameSize.width = screenSize.width; this.setLocation((screenSize.width - frameSize.width)/2 - 40, (screenSize.height - frameSize.height)/2 - 40); this.setResizable(false); this.setTitle(user_profile.contact_url); this.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(WindowEvent e) { this_windowClosing(e); } }); jPanel1.setLayout(borderLayout3); jPanel2.setLayout(borderLayout2); display.setBackground(Color.black); display.setForeground(Color.green); display.setEditable(false); display.setText(app_name); jPanel4.setLayout(borderLayout1); jPanel5.setLayout(gridLayout2); jPanel3.setLayout(gridLayout3); gridLayout3.setRows(2); gridLayout3.setColumns(1); if (icon_call!=null && icon_call.getIconWidth()>0) jButton1.setIcon(icon_call); else jButton1.setText("Call"); jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton1_actionPerformed(); } }); jButton1.addKeyListener(new java.awt.event.KeyAdapter() { public void keyTyped(KeyEvent e) { jButton1_actionPerformed(); } }); if (icon_hangup!=null && icon_hangup.getIconWidth()>0) jButton2.setIcon(icon_hangup); else jButton2.setText("Hungup"); jButton2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jButton2_actionPerformed(); } }); jButton2.addKeyListener(new java.awt.event.KeyAdapter() { public void keyTyped(KeyEvent e) { jButton2_actionPerformed(); } }); jComboBox1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { jComboBox1_actionPerformed(e); } }); comboBoxEditor1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { comboBoxEditor1_actionPerformed(e); } }); jButton2.setFont(new java.awt.Font("Dialog", 0, 10)); jButton1.setFont(new java.awt.Font("Dialog", 0, 10)); comboBoxEditor1.getEditorComponent().setBackground(Color.yellow); jComboBox1.setEditable(true); jComboBox1.setEditor(comboBoxEditor1); jComboBox1.setSelectedItem(null); jPanel3.setPreferredSize(new Dimension(0,C_Height)); this.getContentPane().add(jPanel1, BorderLayout.CENTER); jPanel1.add(jPanel2, BorderLayout.CENTER); jPanel1.add(jPanel3, BorderLayout.SOUTH); jPanel2.add(display, BorderLayout.CENTER); jPanel3.add(jPanel4, null); jPanel3.add(jPanel5, null); jPanel4.add(jComboBox1, BorderLayout.CENTER); jPanel5.add(jButton1, null); jPanel5.add(jButton2, null); // show it this.setVisible(true); } /** Starts the UA */ void run() { // Set the re-invite if (user_profile.re_invite_time>0) { ua.reInvite(user_profile.contact_url,user_profile.re_invite_time); } // Set the transfer (REFER) if (user_profile.transfer_to!=null && user_profile.transfer_time>0) { ua.callTransfer(user_profile.transfer_to,user_profile.transfer_time); } if (user_profile.do_unregister_all) // ########## unregisters ALL contact URLs { ua.printLog("UNREGISTER ALL contact URLs"); unregisterall(); } if (user_profile.do_unregister) // unregisters the contact URL { ua.printLog("UNREGISTER the contact URL"); unregister(); } if (user_profile.do_register) // ########## registers the contact URL with the registrar server { ua.printLog("REGISTRATION"); loopRegister(user_profile.expires,user_profile.expires/2,user_profile.keepalive_time); } if (user_profile.call_to!=null) // ########## make a call with the remote URL { ua.printLog("UAC: CALLING "+user_profile.call_to); jComboBox1.setSelectedItem(null); comboBoxEditor1.setItem(user_profile.call_to); ua.call(user_profile.call_to); } if (!user_profile.audio && !user_profile.video) ua.printLog("ONLY SIGNALING, NO MEDIA"); } void this_windowClosing(WindowEvent e) { System.exit(0); } void jButton1_actionPerformed() { if (ua.statusIs(UserAgent.UA_IDLE)) { String url=(String)comboBoxEditor1.getItem(); if (url!=null && url.length()>0) { ua.hangup(); ua.call(url); display.setText("CALLING "+url); } } else if (ua.statusIs(UserAgent.UA_INCOMING_CALL)) { ua.accept(); display.setText("ON CALL"); } } void jButton2_actionPerformed() { if (!ua.statusIs(UserAgent.UA_IDLE)) { ua.hangup(); ua.listen(); display.setText("HANGUP"); } } void jComboBox1_actionPerformed(ActionEvent e) { // if the edited URL is different from the selected item, copy the selected item in the editor /* String edit_name=(String)comboBoxEditor1.getItem(); int index=jComboBox1.getSelectedIndex(); if (index>=0) { String selected_name=contact_list.elementAt(index); if (!selected_name.equals(edit_name)) comboBoxEditor1.setItem(selected_name); }*/ } void comboBoxEditor1_actionPerformed(ActionEvent e) { // if a new URL has been typed, insert it in the contact_list and make it selected item // else, simply make the URL the selected item String name=(String)comboBoxEditor1.getItem(); // parse separatly NameAddrresses or SipURLs if (name.indexOf("\"")>=0 || name.indexOf("<")>=0) { // try to parse a NameAddrress NameAddress nameaddr=(new SipParser(name)).getNameAddress(); if (nameaddr!=null) name=nameaddr.toString(); else name=null; } else { // try to parse a SipURL SipURL url=new SipURL(name); if (url!=null) name=url.toString(); else name=null; } if (name==null) { System.out.println("DEBUG: No sip url recognized in: "+(String)comboBoxEditor1.getItem()); return; } // checks if the the URL is already present in the contact_list if (!contact_list.contains(name)) { jComboBox1.insertItemAt(name,0); jComboBox1.setSelectedIndex(0); // limit the list size while (contact_list.getElements().size()>NMAX_CONTACTS) jComboBox1.removeItemAt(NMAX_CONTACTS); // save new contact list contact_list.save(); } else { int index=contact_list.indexOf(name); jComboBox1.setSelectedIndex(index); } } /** Gets the UserAgent */ /*protected UserAgent getUA() { return ua; }*/ /** Register with the registrar server * @param expire_time expiration time in seconds */ public void register(int expire_time) { if (ra.isRegistering()) ra.halt(); ra.register(expire_time); } /** Periodically registers the contact address with the registrar server. * @param expire_time expiration time in seconds * @param renew_time renew time in seconds * @param keepalive_time keep-alive packet rate (inter-arrival time) in milliseconds */ public void loopRegister(int expire_time, int renew_time, long keepalive_time) { if (ra.isRegistering()) ra.halt(); ra.loopRegister(expire_time,renew_time,keepalive_time); } /** Unregister with the registrar server */ public void unregister() { if (ra.isRegistering()) ra.halt(); ra.unregister(); } /** Unregister all contacts with the registrar server */ public void unregisterall() { if (ra.isRegistering()) ra.halt(); ra.unregisterall(); } // ********************** UA callback functions ********************** /** When a new call is incoming */ public void onUaCallIncoming(UserAgent ua, NameAddress callee, NameAddress caller) { if (ua.user_profile.redirect_to!=null) // redirect the call { display.setText("CALL redirected to "+ua.user_profile.redirect_to); ua.redirect(ua.user_profile.redirect_to); } else if (ua.user_profile.accept_time>=0) // automatically accept the call { display.setText("ON CALL"); jComboBox1.setSelectedItem(null); comboBoxEditor1.setItem(caller.toString()); //ua.accept(); ua.automaticAccept(ua.user_profile.accept_time); } else { display.setText("INCOMING CALL"); jComboBox1.setSelectedItem(null); comboBoxEditor1.setItem(caller.toString()); } } /** When an ougoing call is remotly ringing */ public void onUaCallRinging(UserAgent ua) { display.setText("RINGING"); } /** When an ougoing call has been accepted */ public void onUaCallAccepted(UserAgent ua) { display.setText("ON CALL"); } /** When an incoming call has been cancelled */ public void onUaCallCancelled(UserAgent ua) { display.setText("CANCELLED"); ua.listen(); } /** When a call has been trasferred */ public void onUaCallTrasferred(UserAgent ua) { display.setText("TRASFERRED"); ua.listen(); } /** When an ougoing call has been refused or timeout */ public void onUaCallFailed(UserAgent ua) { display.setText("FAILED"); ua.listen(); } /** When a call has been locally or remotely closed */ public void onUaCallClosed(UserAgent ua) { display.setText("BYE"); ua.listen(); } // ********************** RA callback functions ********************** /** When a UA has been successfully (un)registered. */ public void onUaRegistrationSuccess(RegisterAgent ra, NameAddress target, NameAddress contact, String result) { ua.printLog("Registration success: "+result,LogLevel.HIGH); this.setTitle(user_profile.from_url); } /** When a UA failed on (un)registering. */ public void onUaRegistrationFailure(RegisterAgent ra, NameAddress target, NameAddress contact, String result) { ua.printLog("Registration failure: "+result,LogLevel.HIGH); this.setTitle(user_profile.contact_url); } // ******************************** MAIN ******************************* /** The main method. */ public static void main(String[] args) { System.out.println("Graphical MJSIP UA 1.0"); String file=null; boolean opt_regist=false; boolean opt_unregist=false; boolean opt_unregist_all=false; int opt_expires=-1; long opt_keepalive_time=-1; boolean opt_no_offer=false; String opt_call_to=null; int opt_accept_time=-1; int opt_hangup_time=-1; String opt_redirect_to=null; String opt_transfer_to=null; int opt_transfer_time=-1; int opt_re_invite_time=-1; boolean opt_audio=false; boolean opt_video=false; int opt_media_port=0; boolean opt_recv_only=false; boolean opt_send_only=false; boolean opt_send_tone=false; String opt_send_file=null; String opt_recv_file=null; boolean opt_no_prompt=false; String opt_from_url=null; String opt_contact_url=null; String opt_username=null; String opt_realm=null; String opt_passwd=null; int opt_debug_level=-1; String opt_log_path=null; String opt_outbound_proxy=null; String opt_via_addr=SipProvider.AUTO_CONFIGURATION; int opt_host_port=SipStack.default_port; try { for (int i=0; i<args.length; i++) { if (args[i].equals("-f") && args.length>(i+1)) { file=args[++i]; continue; } if (args[i].equals("-g") && args.length>(i+1)) // registrate the contact url { opt_regist=true; String time=args[++i]; if (time.charAt(time.length()-1)=='h') opt_expires=Integer.parseInt(time.substring(0,time.length()-1))*3600; else opt_expires=Integer.parseInt(time); continue; } if (args[i].equals("-u")) // unregistrate the contact url { opt_unregist=true; continue; } if (args[i].equals("-z")) // unregistrate all contact urls { opt_unregist_all=true; continue; } if (args[i].equals("-n")) // no offer in the invite { opt_no_offer=true; continue; } if (args[i].equals("-c") && args.length>(i+1)) // make a call with a remote user (url) { opt_call_to=args[++i]; continue; } if (args[i].equals("-y") && args.length>(i+1)) // set automatic accept time { opt_accept_time=Integer.parseInt(args[++i]); continue; } if (args[i].equals("-t") && args.length>(i+1)) // set the call duration { opt_hangup_time=Integer.parseInt(args[++i]); continue; } if (args[i].equals("-i") && args.length>(i+1)) // set the re-invite time { opt_re_invite_time=Integer.parseInt(args[++i]); continue; } if (args[i].equals("-r") && args.length>(i+1)) // redirect the call to a new url { opt_accept_time=0; opt_redirect_to=args[++i]; continue; } if (args[i].equals("-q") && args.length>(i+1)) // transfers the call to a new user (REFER) { opt_transfer_to=args[++i]; opt_transfer_time=Integer.parseInt(args[++i]); continue; } if (args[i].equals("-a")) // use audio { opt_audio=true; continue; } if (args[i].equals("-v")) // use video { opt_video=true; continue; } if (args[i].equals("-m") && args.length>(i+1)) // set the local media port { opt_media_port=Integer.parseInt(args[++i]); continue; } if (args[i].equals("-o") && args.length>(i+1)) // outbound proxy { opt_outbound_proxy=args[++i]; continue; } if (args[i].equals("--via-addr") && args.length>(i+1)) // via addr { opt_via_addr=args[++i]; continue; } if (args[i].equals("-p") && args.length>(i+1)) // set the local sip port { opt_host_port=Integer.parseInt(args[++i]); continue; } if (args[i].equals("--keep-alive") && args.length>(i+1)) // keep-alive { opt_keepalive_time=Integer.parseInt(args[++i]); continue; } if (args[i].equals("--from-url") && args.length>(i+1)) // user's AOR { opt_from_url=args[++i]; continue; } if (args[i].equals("--contact-url") && args.length>(i+1)) // user's contact_url { opt_contact_url=args[++i]; continue; } if (args[i].equals("--username") && args.length>(i+1)) // username { opt_username=args[++i]; continue; } if (args[i].equals("--realm") && args.length>(i+1)) // realm { opt_realm=args[++i]; continue; } if (args[i].equals("--passwd") && args.length>(i+1)) // passwd { opt_passwd=args[++i]; continue; } if (args[i].equals("--recv-only")) // receive only mode { opt_recv_only=true; continue; } if (args[i].equals("--send-only")) // send only mode { opt_send_only=true; continue; } if (args[i].equals("--send-tone")) // send only mode { opt_send_only=true; opt_send_tone=true; continue; } if (args[i].equals("--send-file") && args.length>(i+1)) // send audio file { opt_send_file=args[++i]; continue; } if (args[i].equals("--recv-file") && args.length>(i+1)) // receive audio file { opt_recv_file=args[++i]; continue; } if (args[i].equals("--debug-level") && args.length>(i+1)) // debug level { opt_debug_level=Integer.parseInt(args[++i]); continue; } if (args[i].equals("--log-path") && args.length>(i+1)) // log path { opt_log_path=args[++i]; continue; } if (args[i].equals("--no-prompt")) // do not prompt { opt_no_prompt=true; continue; } // else, do: if (!args[i].equals("-h")) System.out.println("unrecognized param '"+args[i]+"'\n"); System.out.println("usage:\n java GraphicalUA [options]"); System.out.println(" options:"); System.out.println(" -h this help"); System.out.println(" -f <file> specifies a configuration file"); System.out.println(" -g <time> registers the contact URL with the registrar server"); System.out.println(" where time is the duration of the registration, and can be"); System.out.println(" in seconds (default) or hours (-g 7200 is the same as -g 2h)"); System.out.println(" -u unregisters the contact URL with the registrar server"); System.out.println(" (is the same as -g 0)"); System.out.println(" -z unregisters ALL the contact URLs"); System.out.println(" -n no offer in invite (offer/answer in 2xx/ack)"); System.out.println(" -c <call_to> calls a remote user"); System.out.println(" -y <secs> auto answer time"); System.out.println(" -t <secs> auto hangup time (0 means manual hangup)"); System.out.println(" -i <secs> re-invite after <secs> seconds"); System.out.println(" -r <url> redirects the call to new user <url>"); System.out.println(" -q <url> <secs> transfers the call to <url> after <secs> seconds"); System.out.println(" -a audio"); System.out.println(" -v video"); System.out.println(" -m <port> (first) local media port"); System.out.println(" -p <port> local SIP port, used ONLY without -f option"); System.out.println(" -o <addr>[:<port>] use the specified outbound proxy"); System.out.println(" --via-addr <addr> host via address, used ONLY without -f option"); System.out.println(" --keep-alive <millisecs> send keep-alive packets each <millisecs>"); System.out.println(" --from-url <url> user's address-of-record (AOR)"); System.out.println(" --contact-url <url> user's contact url"); System.out.println(" --username <name> user name used for authentication"); System.out.println(" --realm <realm> realm used for authentication"); System.out.println(" --passwd <passwd> passwd used for authentication"); System.out.println(" --recv-only receive only mode, no media is sent"); System.out.println(" --send-only send only mode, no media is received"); System.out.println(" --send-tone send only mode, an audio test tone is generated"); System.out.println(" --send-file <file> audio is played from the specified file"); System.out.println(" --recv-file <file> audio is recorded to the specified file"); System.out.println(" --debug-level <n> debug level (level=0 means no log)"); System.out.println(" --log-path <path> base path for all logs (./log is the default value)"); System.out.println(" --no-prompt do not prompt"); System.exit(0); } SipStack.init(file); if (opt_debug_level>=0) SipStack.debug_level=opt_debug_level; if (opt_log_path!=null) SipStack.log_path=opt_log_path; SipProvider sip_provider; if (file!=null) sip_provider=new SipProvider(file); else sip_provider=new SipProvider(opt_via_addr,opt_host_port); if (opt_outbound_proxy!=null) sip_provider.setOutboundProxy(new SocketAddress(opt_outbound_proxy)); UserAgentProfile user_profile=new UserAgentProfile(file); if (opt_regist) user_profile.do_register=true; if (opt_unregist) user_profile.do_unregister=true; if (opt_unregist_all) user_profile.do_unregister_all=true; if (opt_expires>0) user_profile.expires=opt_expires; if (opt_keepalive_time>=0) user_profile.keepalive_time=opt_keepalive_time; if (opt_hangup_time>0) user_profile.hangup_time=opt_hangup_time; if (opt_no_offer) user_profile.no_offer=true; if (opt_call_to!=null) user_profile.call_to=opt_call_to; if (opt_accept_time>=0) user_profile.accept_time=opt_accept_time; if (opt_redirect_to!=null) user_profile.redirect_to=opt_redirect_to; if (opt_re_invite_time>0) user_profile.re_invite_time=opt_re_invite_time; if (opt_transfer_to!=null) user_profile.transfer_to=opt_transfer_to; if (opt_transfer_time>0) user_profile.transfer_time=opt_transfer_time; if (opt_audio) user_profile.audio=true; if (opt_video) user_profile.video=true; if (opt_media_port>0) user_profile.video_port=(user_profile.audio_port=opt_media_port)+2; if (opt_from_url!=null) user_profile.from_url=opt_from_url; if (opt_contact_url!=null) user_profile.contact_url=opt_contact_url; if (opt_username!=null) user_profile.username=opt_username; if (opt_realm!=null) user_profile.realm=opt_realm; if (opt_passwd!=null) user_profile.passwd=opt_passwd; if (opt_recv_only) user_profile.recv_only=true; if (opt_send_only) user_profile.send_only=true; if (opt_send_tone) user_profile.send_tone=true; if (opt_send_file!=null) user_profile.send_file=opt_send_file; if (opt_recv_file!=null) user_profile.recv_file=opt_recv_file; if (opt_no_prompt) user_profile.no_prompt=true; // use audio as default media in case of.. if ((opt_recv_only || opt_send_only || opt_send_tone || opt_send_file!=null || opt_recv_file!=null) && !opt_video) user_profile.audio=true; new GraphicalUA(sip_provider,user_profile); } catch (Exception e) { e.printStackTrace(); } } // ****************************** Logs ***************************** /** Adds a new string to the default Log */ private void printLog(String str) { printLog(str,LogLevel.HIGH); } /** Adds a new string to the default Log */ private void printLog(String str, int level) { if (log!=null) log.println("GraphicalUA: "+str,level+SipStack.LOG_LEVEL_UA); } /** Adds the Exception message to the default Log */ private void printException(Exception e,int level) { if (log!=null) log.printException(e,level+SipStack.LOG_LEVEL_UA); } }