Thu Oct 11 06:47:04 2012

Asterisk developer's documentation


app_rpt.c

Go to the documentation of this file.
00001 #define  NEW_ASTERISK
00002 /* #define OLD_ASTERISK */
00003 /*
00004  * Asterisk -- An open source telephony toolkit.
00005  *
00006  * Copyright (C) 2002-2008, Jim Dixon, WB6NIL
00007  *
00008  * Jim Dixon, WB6NIL <jim@lambdatel.com>
00009  * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 /*! \file
00022  *
00023  * \brief Radio Repeater / Remote Base program 
00024  *  version 0.115 5/12/08 2055 EDT
00025  * 
00026  * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
00027  *
00028  * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
00029  * \note Steven Henke, W9SH, <w9sh@arrl.net> added a few features here and there.
00030  *
00031  * See http://www.zapatatelephony.org/app_rpt.html
00032  *
00033  *
00034  * Repeater / Remote Functions:
00035  * "Simple" Mode:  * - autopatch access, # - autopatch hangup
00036  * Normal mode:
00037  * See the function list in rpt.conf (autopatchup, autopatchdn)
00038  * autopatchup can optionally take comma delimited setting=value pairs:
00039  *  
00040  *
00041  * context=string    :  Override default context with "string"
00042  * dialtime=ms       :  Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
00043  * farenddisconnect=1      :  Automatically disconnect when called party hangs up
00044  * noct=1         :  Don't send repeater courtesy tone during autopatch calls
00045  * quiet=1        :  Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
00046  *
00047  *
00048  * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
00049  *
00050  *  To send an asterisk (*) while dialing or talking on phone,
00051  *  use the autopatch acess code.
00052  *
00053  *
00054  * status cmds:
00055  *
00056  *  1 - Force ID (global)
00057  *  2 - Give Time of Day (global)
00058  *  3 - Give software Version (global)
00059  *  11 - Force ID (local only)
00060  *  12 - Give Time of Day (local only)
00061  *
00062  * cop (control operator) cmds:
00063  *
00064  *  1 - System warm boot
00065  *  2 - System enable
00066  *  3 - System disable
00067  *  4 - Test Tone On/Off
00068  *  5 - Dump System Variables on Console (debug)
00069  *  6 - PTT (phone mode only)
00070  *  7 - Time out timer enable
00071  *  8 - Time out timer disable
00072  *  9 - Autopatch enable
00073  *  10 - Autopatch disable
00074  *  11 - Link enable
00075  *  12 - Link disable
00076  *  13 - Query System State
00077  *  14 - Change System State
00078  *  15 - Scheduler Enable
00079  *  16 - Scheduler Disable
00080  *  17 - User functions (time, id, etc) enable
00081  *  18 - User functions (time, id, etc) disable
00082  *  19 - Select alternate hang timer
00083  *  20 - Select standard hang timer 
00084  *  21 - Enable Parrot Mode
00085  *  22 - Disable Parrot Mode
00086  *  23 - Birdbath (Current Parrot Cleanup/Flush)
00087  *  24 - Flush all telemetry
00088  *  25 - Query last node un-keyed
00089  *  26 - Query all nodes keyed/unkeyed
00090  *  30 - Recall Memory Setting in Attached Xcvr
00091  *  31 - Channel Selector for Parallel Programmed Xcvr
00092  *  32 - Touchtone pad test: command + Digit string + # to playback all digits pressed
00093  *
00094  * ilink cmds:
00095  *
00096  *  1 - Disconnect specified link
00097  *  2 - Connect specified link -- monitor only
00098  *  3 - Connect specified link -- tranceive
00099  *  4 - Enter command mode on specified link
00100  *  5 - System status
00101  *  6 - Disconnect all links
00102  *  11 - Disconnect a previously permanently connected link
00103  *  12 - Permanently connect specified link -- monitor only
00104  *  13 - Permanently connect specified link -- tranceive
00105  *  15 - Full system status (all nodes)
00106  *  16 - Reconnect links disconnected with "disconnect all links"
00107  *  200 thru 215 - (Send DTMF 0-9,*,#,A-D) (200=0, 201=1, 210=*, etc)
00108  *
00109  * remote cmds:
00110  *
00111  *  1 - Recall Memory MM  (*000-*099) (Gets memory from rpt.conf)
00112  *  2 - Set VFO MMMMM*KKK*O   (Mhz digits, Khz digits, Offset)
00113  *  3 - Set Rx PL Tone HHH*D*
00114  *  4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
00115  *  5 - Link Status (long)
00116  *  6 - Set operating mode M (FM, USB, LSB, AM, etc)
00117  *  100 - RX PL off (Default)
00118  *  101 - RX PL On
00119  *  102 - TX PL Off (Default)
00120  *  103 - TX PL On
00121  *  104 - Low Power
00122  *  105 - Med Power
00123  *  106 - Hi Power
00124  *  107 - Bump Down 20 Hz
00125  *  108 - Bump Down 100 Hz
00126  *  109 - Bump Down 500 Hz
00127  *  110 - Bump Up 20 Hz
00128  *  111 - Bump Up 100 Hz
00129  *  112 - Bump Up 500 Hz
00130  *  113 - Scan Down Slow
00131  *  114 - Scan Down Medium
00132  *  115 - Scan Down Fast
00133  *  116 - Scan Up Slow
00134  *  117 - Scan Up Medium
00135  *  118 - Scan Up Fast
00136  *  119 - Transmit allowing auto-tune
00137  *  140 - Link Status (brief)
00138  *  200 thru 215 - (Send DTMF 0-9,*,#,A-D) (200=0, 201=1, 210=*, etc)
00139  *
00140  * playback cmds:
00141  *  specify the name of the file to be played (for example, 25=rpt/foo)
00142  *
00143  *
00144  * 'duplex' modes:  (defaults to duplex=2)
00145  *
00146  * 0 - Only remote links key Tx and no main repeat audio.
00147  * 1 - Everything other then main Rx keys Tx, no main repeat audio.
00148  * 2 - Normal mode
00149  * 3 - Normal except no main repeat audio.
00150  * 4 - Normal except no main repeat audio during autopatch only
00151  *
00152 */
00153 
00154 /*** MODULEINFO
00155    <depend>dahdi</depend>
00156    <depend>tonezone</depend>
00157    <defaultenabled>no</defaultenabled>
00158  ***/
00159 
00160 /* Un-comment the following to include support for MDC-1200 digital tone
00161    signalling protocol (using KA6SQG's GPL'ed implementation) */
00162 /* #include "mdc_decode.c" */
00163 
00164 /* Un-comment the following to include support for notch filters in the
00165    rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
00166 /* #include "rpt_notch.c" */
00167 
00168 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
00169 
00170 #ifdef OLD_ASTERISK
00171 #define ast_free free
00172 #define ast_malloc malloc
00173 #define ast_strdup strdup
00174 #endif
00175 
00176 
00177 #define  MAXDTMF 32
00178 #define  MAXMACRO 2048
00179 #define  MAXLINKLIST 512
00180 #define  LINKLISTTIME 10000
00181 #define  LINKLISTSHORTTIME 200
00182 #define  LINKPOSTTIME 30000
00183 #define  LINKPOSTSHORTTIME 200
00184 #define  KEYPOSTTIME 30000
00185 #define  KEYPOSTSHORTTIME 200
00186 #define  MACROTIME 100
00187 #define  MACROPTIME 500
00188 #define  DTMF_TIMEOUT 3
00189 #define  KENWOOD_RETRIES 5
00190 #define  TOPKEYN 32
00191 #define  TOPKEYWAIT 3
00192 #define  TOPKEYMAXSTR 30
00193 
00194 #define  AUTHTELLTIME 7000
00195 #define  AUTHTXTIME 1000
00196 #define  AUTHLOGOUTTIME 25000
00197 
00198 #ifdef   __RPT_NOTCH
00199 #define  MAXFILTERS 10
00200 #endif
00201 
00202 #define  DISC_TIME 10000  /* report disc after 10 seconds of no connect */
00203 #define  MAX_RETRIES 5
00204 #define  MAX_RETRIES_PERM 1000000000
00205 
00206 #define  REDUNDANT_TX_TIME 2000
00207 
00208 #define  RETRY_TIMER_MS 5000
00209 
00210 #define  PATCH_DIALPLAN_TIMEOUT 1500
00211 
00212 #ifdef OLD_ASTERISK
00213 #define  START_DELAY 10
00214 #else
00215 #define  START_DELAY 2
00216 #endif
00217 
00218 #define  RPT_LOCKOUT_SECS 10
00219 
00220 #define MAXPEERSTR 31
00221 #define  MAXREMSTR 15
00222 
00223 #define  DELIMCHR ','
00224 #define  QUOTECHR 34
00225 
00226 #define  MONITOR_DISK_BLOCKS_PER_MINUTE 38
00227 
00228 #define  DEFAULT_MONITOR_MIN_DISK_BLOCKS 10000
00229 #define  DEFAULT_REMOTE_INACT_TIMEOUT (15 * 60)
00230 #define  DEFAULT_REMOTE_TIMEOUT (60 * 60)
00231 #define  DEFAULT_REMOTE_TIMEOUT_WARNING (3 * 60)
00232 #define  DEFAULT_REMOTE_TIMEOUT_WARNING_FREQ 30
00233 
00234 #define  NODES "nodes"
00235 #define  EXTNODES "extnodes"
00236 #define MEMORY "memory"
00237 #define MACRO "macro"
00238 #define  FUNCTIONS "functions"
00239 #define TELEMETRY "telemetry"
00240 #define MORSE "morse"
00241 #define  TONEMACRO "tonemacro"
00242 #define  FUNCCHAR '*'
00243 #define  ENDCHAR '#'
00244 #define  EXTNODEFILE "/var/lib/asterisk/rpt_extnodes"
00245 #define  NODENAMES "rpt/nodenames"
00246 #define  PARROTFILE "/tmp/parrot_%s_%u"
00247 
00248 #define  PARROTTIME 1000
00249 
00250 #define  DEFAULT_IOBASE 0x378
00251 
00252 #define  DEFAULT_CIV_ADDR 0x58
00253 
00254 #define  MAXCONNECTTIME 5000
00255 
00256 #define MAXNODESTR 300
00257 
00258 #define MAXNODELEN 16
00259 
00260 #define MAXIDENTLEN 32
00261 
00262 #define MAXPATCHCONTEXT 100
00263 
00264 #define ACTIONSIZE 32
00265 
00266 #define TELEPARAMSIZE 256
00267 
00268 #define REM_SCANTIME 100
00269 
00270 #define  DTMF_LOCAL_TIME 250
00271 #define  DTMF_LOCAL_STARTTIME 500
00272 
00273 #define  IC706_PL_MEMORY_OFFSET 50
00274 
00275 #define  VOX_ON_DEBOUNCE_COUNT 3
00276 #define  VOX_OFF_DEBOUNCE_COUNT 20
00277 #define  VOX_MAX_THRESHOLD 10000.0
00278 #define  VOX_MIN_THRESHOLD 3000.0
00279 #define  VOX_TIMEOUT_MS 5000
00280 #define  VOX_RECOVER_MS 500
00281 #define  SIMPLEX_PATCH_DELAY 25
00282 #define  SIMPLEX_PHONE_DELAY 25
00283 
00284 #define  STATPOST_PROGRAM "/usr/bin/wget,-q,--output-document=/dev/null,--no-check-certificate"
00285 
00286 #define  ALLOW_LOCAL_CHANNELS
00287 
00288 enum {REM_OFF,REM_MONITOR,REM_TX};
00289 
00290 enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
00291    CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME, PLAYBACK,
00292    STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
00293    TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY, FULLSTATUS,
00294    MEMNOTFOUND, INVFREQ, REMMODE, REMLOGIN, REMXXX, REMSHORTSTATUS,
00295    REMLONGSTATUS, LOGINREQ, SCAN, SCANSTAT, TUNE, SETREMOTE, TOPKEY,
00296    TIMEOUT_WARNING, ACT_TIMEOUT_WARNING, LINKUNKEY, UNAUTHTX, PARROT,
00297    STATS_TIME_LOCAL};
00298 
00299 
00300 enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
00301 
00302 enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR};
00303 
00304 enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_COMPLETEQUIET, DC_DOKEY};
00305 
00306 enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE, SOURCE_ALT};
00307 
00308 enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM, DLY_COMP, DLY_LINKUNKEY, DLY_PARROT};
00309 
00310 enum {REM_MODE_FM,REM_MODE_USB,REM_MODE_LSB,REM_MODE_AM};
00311 
00312 enum {HF_SCAN_OFF,HF_SCAN_DOWN_SLOW,HF_SCAN_DOWN_QUICK,
00313       HF_SCAN_DOWN_FAST,HF_SCAN_UP_SLOW,HF_SCAN_UP_QUICK,HF_SCAN_UP_FAST};
00314 
00315 #include "asterisk.h"
00316 
00317 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211580 $")
00318 
00319 #include <signal.h>
00320 #include <stdio.h>
00321 #include <stdint.h>
00322 #include <unistd.h>
00323 #include <string.h>
00324 #include <stdlib.h>
00325 #include <search.h>
00326 #include <sys/types.h>
00327 #include <sys/stat.h>
00328 #include <errno.h>
00329 #include <dirent.h>
00330 #include <ctype.h>
00331 #include <sys/stat.h>
00332 #include <sys/time.h>
00333 #include <sys/file.h>
00334 #include <sys/ioctl.h>
00335 #ifdef HAVE_SYS_IO_H
00336 #include <sys/io.h>
00337 #endif
00338 #include <sys/vfs.h>
00339 #include <math.h>
00340 #include <dahdi/user.h>
00341 #include <dahdi/tonezone.h>
00342 #include <netinet/in.h>
00343 #include <arpa/inet.h>
00344 
00345 #include "asterisk/utils.h"
00346 #include "asterisk/lock.h"
00347 #include "asterisk/file.h"
00348 #include "asterisk/logger.h"
00349 #include "asterisk/channel.h"
00350 #include "asterisk/callerid.h"
00351 #include "asterisk/pbx.h"
00352 #include "asterisk/module.h"
00353 #include "asterisk/translate.h"
00354 #include "asterisk/features.h"
00355 #include "asterisk/options.h"
00356 #include "asterisk/cli.h"
00357 #include "asterisk/config.h"
00358 #include "asterisk/say.h"
00359 #include "asterisk/localtime.h"
00360 #include "asterisk/cdr.h"
00361 #include "asterisk/options.h"
00362 #include "asterisk/manager.h"
00363 #include "asterisk/app.h"
00364 
00365 #include <termios.h>
00366 
00367 #ifdef   NEW_ASTERISK
00368 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
00369 #endif
00370 
00371 
00372 /* Start a tone-list going */
00373 int ast_playtones_start(struct ast_channel *chan, int vol, const char* tonelist, int interruptible);
00374 /*! Stop the tones from playing */
00375 void ast_playtones_stop(struct ast_channel *chan);
00376 
00377 static  char *tdesc = "Radio Repeater / Remote Base  version 0.115  5/12/2008";
00378 
00379 static char *app = "Rpt";
00380 
00381 static char *synopsis = "Radio Repeater/Remote Base Control System";
00382 
00383 static char *descrip = 
00384 "  Rpt(nodename[|options][|M][|*]):  \n"
00385 "    Radio Remote Link or Remote Base Link Endpoint Process.\n"
00386 "\n"
00387 "    Not specifying an option puts it in normal endpoint mode (where source\n"
00388 "    IP and nodename are verified).\n"
00389 "\n"
00390 "    Options are as follows:\n"
00391 "\n"
00392 "        X - Normal endpoint mode WITHOUT security check. Only specify\n"
00393 "            this if you have checked security already (like with an IAX2\n"
00394 "            user/password or something).\n"
00395 "\n"
00396 "        Rannounce-string[|timeout[|timeout-destination]] - Amateur Radio\n"
00397 "            Reverse Autopatch. Caller is put on hold, and announcement (as\n"
00398 "            specified by the 'announce-string') is played on radio system.\n"
00399 "            Users of radio system can access autopatch, dial specified\n"
00400 "            code, and pick up call. Announce-string is list of names of\n"
00401 "            recordings, or \"PARKED\" to substitute code for un-parking,\n"
00402 "            or \"NODE\" to substitute node number.\n"
00403 "\n"
00404 "        P - Phone Control mode. This allows a regular phone user to have\n"
00405 "            full control and audio access to the radio system. For the\n"
00406 "            user to have DTMF control, the 'phone_functions' parameter\n"
00407 "            must be specified for the node in 'rpt.conf'. An additional\n"
00408 "            function (cop,6) must be listed so that PTT control is available.\n"
00409 "\n"
00410 "        D - Dumb Phone Control mode. This allows a regular phone user to\n"
00411 "            have full control and audio access to the radio system. In this\n"
00412 "            mode, the PTT is activated for the entire length of the call.\n"
00413 "            For the user to have DTMF control (not generally recomended in\n"
00414 "            this mode), the 'dphone_functions' parameter must be specified\n"
00415 "            for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
00416 "            available to the phone user.\n"
00417 "\n"
00418 "        S - Simplex Dumb Phone Control mode. This allows a regular phone user\n"
00419 "            audio-only access to the radio system. In this mode, the\n"
00420 "            transmitter is toggled on and off when the phone user presses the\n"
00421 "            funcchar (*) key on the telephone set. In addition, the transmitter\n"
00422 "            will turn off if the endchar (#) key is pressed. When a user first\n"
00423 "            calls in, the transmitter will be off, and the user can listen for\n"
00424 "            radio traffic. When the user wants to transmit, they press the *\n" 
00425 "            key, start talking, then press the * key again or the # key to turn\n"
00426 "            the transmitter off.  No other functions can be executed by the\n"
00427 "            user on the phone when this mode is selected. Note: If your\n"
00428 "            radio system is full-duplex, we recommend using either P or D\n"
00429 "            modes as they provide more flexibility.\n"
00430 "\n"
00431 "        q - Query Status. Sets channel variables and returns + 101 in plan.\n"
00432 "\n"
00433 "        M - Memory Channel Steer as MXX where XX is the memory channel number.\n"
00434 "\n"
00435 "        * - Alt Macro to execute (e.g. *7 for status)\n"
00436 "\n";
00437 ;
00438 
00439 static int debug = 0;  /* Set this >0 for extra debug output */
00440 static int nrpts = 0;
00441 
00442 static char remdtmfstr[] = "0123456789*#ABCD";
00443 
00444 enum {TOP_TOP,TOP_WON,WON_BEFREAD,BEFREAD_AFTERREAD};
00445 
00446 int max_chan_stat [] = {22000,1000,22000,100,22000,2000,22000};
00447 
00448 #define NRPTSTAT 7
00449 
00450 struct rpt_chan_stat
00451 {
00452    struct timeval last;
00453    long long total;
00454    unsigned long count;
00455    unsigned long largest;
00456    struct timeval largest_time;
00457 };
00458 
00459 char *discstr = "!!DISCONNECT!!";
00460 char *newkeystr = "!NEWKEY!";
00461 static char *remote_rig_ft897="ft897";
00462 static char *remote_rig_rbi="rbi";
00463 static char *remote_rig_kenwood="kenwood";
00464 static char *remote_rig_tm271="tm271";
00465 static char *remote_rig_ic706="ic706";
00466 static char *remote_rig_rtx150="rtx150";
00467 static char *remote_rig_rtx450="rtx450";
00468 static char *remote_rig_ppp16="ppp16";       // parallel port programmable 16 channels
00469 
00470 #define ISRIG_RTX(x) ((!strcmp(x,remote_rig_rtx150)) || (!strcmp(x,remote_rig_rtx450)))
00471 #define  IS_XPMR(x) (!strncasecmp(x->rxchanname,"rad",3))
00472 
00473 #ifdef   OLD_ASTERISK
00474 STANDARD_LOCAL_USER;
00475 LOCAL_USER_DECL;
00476 #endif
00477 
00478 #define  MSWAIT 200
00479 #define  HANGTIME 5000
00480 #define  TOTIME 180000
00481 #define  IDTIME 300000
00482 #define  MAXRPTS 20
00483 #define MAX_STAT_LINKS 32
00484 #define POLITEID 30000
00485 #define FUNCTDELAY 1500
00486 
00487 #define  MAXXLAT 20
00488 #define  MAXXLATTIME 3
00489 
00490 #define MAX_SYSSTATES 10
00491 
00492 struct vox {
00493    float speech_energy;
00494    float noise_energy;
00495    int   enacount;
00496    char  voxena;
00497    char  lastvox;
00498    int   offdebcnt;
00499    int   ondebcnt;
00500 } ;
00501 
00502 #define  mymax(x,y) ((x > y) ? x : y)
00503 #define  mymin(x,y) ((x < y) ? x : y)
00504 
00505 struct rpt_topkey
00506 {
00507 char  node[TOPKEYMAXSTR];
00508 int   timesince;
00509 int   keyed;
00510 } ;
00511 
00512 struct rpt_xlat
00513 {
00514 char  funccharseq[MAXXLAT];
00515 char  endcharseq[MAXXLAT];
00516 char  passchars[MAXXLAT];
00517 int   funcindex;
00518 int   endindex;
00519 time_t   lastone;
00520 } ;
00521 
00522 static time_t  starttime = 0;
00523 
00524 static  pthread_t rpt_master_thread;
00525 
00526 struct rpt;
00527 
00528 struct rpt_link
00529 {
00530    struct rpt_link *next;
00531    struct rpt_link *prev;
00532    char  mode;       /* 1 if in tx mode */
00533    char  isremote;
00534    char  phonemode;
00535    char  phonevox;      /* vox the phone */
00536    char  name[MAXNODESTR]; /* identifier (routing) string */
00537    char  lasttx;
00538    char  lasttx1;
00539    char  lastrx;
00540    char  lastrealrx;
00541    char  lastrx1;
00542    char  connected;
00543    char  hasconnected;
00544    char  perma;
00545    char  thisconnected;
00546    char  outbound;
00547    char  disced;
00548    char  killme;
00549    long  elaptime;
00550    long  disctime;
00551    long  retrytimer;
00552    long  retxtimer;
00553    long  rerxtimer;
00554    int   retries;
00555    int   max_retries;
00556    int   reconnects;
00557    long long connecttime;
00558    struct ast_channel *chan;  
00559    struct ast_channel *pchan; 
00560    char  linklist[MAXLINKLIST];
00561    time_t   linklistreceived;
00562    long  linklisttimer;
00563    int   dtmfed;
00564    int linkunkeytocttimer;
00565    struct timeval lastlinktv;
00566    struct   ast_frame *lastf1,*lastf2;
00567    struct   rpt_chan_stat chan_stat[NRPTSTAT];
00568    struct vox vox;
00569    char wasvox;
00570    int voxtotimer;
00571    char voxtostate;
00572    char newkey;
00573 #ifdef OLD_ASTERISK
00574         AST_LIST_HEAD(, ast_frame) rxq;
00575 #else
00576    AST_LIST_HEAD_NOLOCK(, ast_frame) rxq;
00577 #endif
00578 } ;
00579 
00580 struct rpt_lstat
00581 {
00582    struct   rpt_lstat *next;
00583    struct   rpt_lstat *prev;
00584    char  peer[MAXPEERSTR];
00585    char  name[MAXNODESTR];
00586    char  mode;
00587    char  outbound;
00588    char  reconnects;
00589    char  thisconnected;
00590    long long   connecttime;
00591    struct   rpt_chan_stat chan_stat[NRPTSTAT];
00592 } ;
00593 
00594 struct rpt_tele
00595 {
00596    struct rpt_tele *next;
00597    struct rpt_tele *prev;
00598    struct rpt *rpt;
00599    struct ast_channel *chan;
00600    int   mode;
00601    struct rpt_link mylink;
00602    char param[TELEPARAMSIZE];
00603    intptr_t submode;
00604    uintptr_t  parrot;
00605    pthread_t threadid;
00606 } ;
00607 
00608 struct function_table_tag
00609 {
00610    char action[ACTIONSIZE];
00611    int (*function)(struct rpt *myrpt, char *param, char *digitbuf, 
00612       int command_source, struct rpt_link *mylink);
00613 } ;
00614 
00615 /* Used to store the morse code patterns */
00616 
00617 struct morse_bits
00618 {       
00619    int len;
00620    int ddcomb;
00621 } ;
00622 
00623 struct telem_defaults
00624 {
00625    char name[20];
00626    char value[80];
00627 } ;
00628 
00629 
00630 struct sysstate
00631 {
00632    char txdisable;
00633    char totdisable;
00634    char linkfundisable;
00635    char autopatchdisable;
00636    char schedulerdisable;
00637    char userfundisable;
00638    char alternatetail;
00639 };
00640 
00641 /* rpt cmd support */
00642 #define CMD_DEPTH 1
00643 #define CMD_STATE_IDLE 0
00644 #define CMD_STATE_BUSY 1
00645 #define CMD_STATE_READY 2
00646 #define CMD_STATE_EXECUTING 3
00647 
00648 struct rpt_cmd_struct
00649 {
00650     int state;
00651     int functionNumber;
00652     char param[MAXDTMF];
00653     char digits[MAXDTMF];
00654     int command_source;
00655 };
00656 
00657 static struct rpt
00658 {
00659    ast_mutex_t lock;
00660    ast_mutex_t remlock;
00661    ast_mutex_t statpost_lock;
00662    struct ast_config *cfg;
00663    char reload;
00664    char xlink;                         // cross link state of a share repeater/remote radio
00665    unsigned int statpost_seqno;
00666 
00667    char *name;
00668    char *rxchanname;
00669    char *txchanname;
00670    char remote;
00671    char *remoterig;
00672    struct   rpt_chan_stat chan_stat[NRPTSTAT];
00673    unsigned int scram;
00674 
00675    struct {
00676       char *ourcontext;
00677       char *ourcallerid;
00678       char *acctcode;
00679       char *ident;
00680       char *tonezone;
00681       char simple;
00682       char *functions;
00683       char *link_functions;
00684       char *phone_functions;
00685       char *dphone_functions;
00686       char *alt_functions;
00687       char *nodes;
00688       char *extnodes;
00689       char *extnodefile;
00690       int hangtime;
00691       int althangtime;
00692       int totime;
00693       int idtime;
00694       int tailmessagetime;
00695       int tailsquashedtime;
00696       int duplex;
00697       int politeid;
00698       char *tailmessages[500];
00699       int tailmessagemax;
00700       char  *memory;
00701       char  *macro;
00702       char  *tonemacro;
00703       char  *startupmacro;
00704       int iobase;
00705       char *ioport;
00706       char funcchar;
00707       char endchar;
00708       char nobusyout;
00709       char notelemtx;
00710       char propagate_dtmf;
00711       char propagate_phonedtmf;
00712       char linktolink;
00713       unsigned char civaddr;
00714       struct rpt_xlat inxlat;
00715       struct rpt_xlat outxlat;
00716       char *archivedir;
00717       int authlevel;
00718       char *csstanzaname;
00719       char *skedstanzaname;
00720       char *txlimitsstanzaname;
00721       long monminblocks;
00722       int remoteinacttimeout;
00723       int remotetimeout;
00724       int remotetimeoutwarning;
00725       int remotetimeoutwarningfreq;
00726       int sysstate_cur;
00727       struct sysstate s[MAX_SYSSTATES];
00728       char parrotmode;
00729       int parrottime;
00730       char *rptnode;
00731       char remote_mars;
00732       int voxtimeout_ms;
00733       int voxrecover_ms;
00734       int simplexpatchdelay;
00735       int simplexphonedelay;
00736       char *statpost_program;
00737       char *statpost_url;
00738    } p;
00739    struct rpt_link links;
00740    int unkeytocttimer;
00741    time_t lastkeyedtime;
00742    time_t lasttxkeyedtime;
00743    char keyed;
00744    char txkeyed;
00745    char exttx;
00746    char localtx;
00747    char remoterx;
00748    char remotetx;
00749    char remoteon;
00750    char remtxfreqok;
00751    char tounkeyed;
00752    char tonotify;
00753    char dtmfbuf[MAXDTMF];
00754    char macrobuf[MAXMACRO];
00755    char rem_dtmfbuf[MAXDTMF];
00756    char lastdtmfcommand[MAXDTMF];
00757    char cmdnode[50];
00758    char nowchan;                 // channel now
00759    char waschan;                 // channel selected initially or by command
00760    char bargechan;                  // barge in channel
00761    char macropatch;              // autopatch via tonemacro state
00762    char parrotstate;
00763    int  parrottimer;
00764    unsigned int parrotcnt;
00765    struct ast_channel *rxchannel,*txchannel, *monchannel, *parrotchannel;
00766    struct ast_channel *pchannel,*txpchannel, *dahdirxchannel, *dahditxchannel;
00767    struct ast_channel *voxchannel;
00768    struct ast_frame *lastf1,*lastf2;
00769    struct rpt_tele tele;
00770    struct timeval lasttv,curtv;
00771    pthread_t rpt_call_thread,rpt_thread;
00772    time_t dtmf_time,rem_dtmf_time,dtmf_time_rem;
00773    int calldigittimer;
00774    int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer,tmsgtimer,skedtimer;
00775    int mustid,tailid;
00776    int tailevent;
00777    int telemrefcount;
00778    int dtmfidx,rem_dtmfidx;
00779    int dailytxtime,dailykerchunks,totalkerchunks,dailykeyups,totalkeyups,timeouts;
00780    int totalexecdcommands, dailyexecdcommands;
00781    long  retxtimer;
00782    long  rerxtimer;
00783    long long totaltxtime;
00784    char mydtmf;
00785    char exten[AST_MAX_EXTENSION];
00786    char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
00787    char offset;
00788    char powerlevel;
00789    char txplon;
00790    char rxplon;
00791    char remmode;
00792    char tunerequest;
00793    char hfscanmode;
00794    int hfscanstatus;
00795    char hfscanstop;
00796    char lastlinknode[MAXNODESTR];
00797    char savednodes[MAXNODESTR];
00798    int stopgen;
00799    char patchfarenddisconnect;
00800    char patchnoct;
00801    char patchquiet;
00802    char patchcontext[MAXPATCHCONTEXT];
00803    int patchdialtime;
00804    int macro_longest;
00805    int phone_longestfunc;
00806    int alt_longestfunc;
00807    int dphone_longestfunc;
00808    int link_longestfunc;
00809    int longestfunc;
00810    int longestnode;
00811    int threadrestarts;     
00812    int tailmessagen;
00813    time_t disgorgetime;
00814    time_t lastthreadrestarttime;
00815    long  macrotimer;
00816    char  lastnodewhichkeyedusup[MAXNODESTR];
00817    int   dtmf_local_timer;
00818    char  dtmf_local_str[100];
00819    struct ast_filestream *monstream,*parrotstream;
00820    char  loginuser[50];
00821    char  loginlevel[10];
00822    long  authtelltimer;
00823    long  authtimer;
00824    int iofd;
00825    time_t start_time,last_activity_time;
00826    char  lasttone[32];
00827    struct rpt_tele *active_telem;
00828    struct   rpt_topkey topkey[TOPKEYN];
00829    int topkeystate;
00830    time_t topkeytime;
00831    int topkeylong;
00832    struct vox vox;
00833    char wasvox;
00834    int voxtotimer;
00835    char voxtostate;
00836    int linkposttimer;         
00837    int keyposttimer;       
00838    char newkey;
00839    char inpadtest;
00840 #ifdef OLD_ASTERISK
00841    AST_LIST_HEAD(, ast_frame) txq;
00842 #else
00843    AST_LIST_HEAD_NOLOCK(, ast_frame) txq;
00844 #endif
00845    char txrealkeyed;
00846 #ifdef   __RPT_NOTCH
00847    struct rptfilter
00848    {
00849       char  desc[100];
00850       float x0;
00851       float x1;
00852       float x2;
00853       float y0;
00854       float y1;
00855       float y2;
00856       float gain;
00857       float const0;
00858       float const1;
00859       float const2;
00860    } filters[MAXFILTERS];
00861 #endif
00862 #ifdef   _MDC_DECODE_H_
00863    mdc_decoder_t *mdc;
00864    unsigned short lastunit;
00865 #endif
00866    struct rpt_cmd_struct cmdAction;
00867 } rpt_vars[MAXRPTS]; 
00868 
00869 struct nodelog {
00870 struct nodelog *next;
00871 struct nodelog *prev;
00872 time_t   timestamp;
00873 char archivedir[MAXNODESTR];
00874 char str[MAXNODESTR * 2];
00875 } nodelog;
00876 
00877 static int service_scan(struct rpt *myrpt);
00878 static int set_mode_ft897(struct rpt *myrpt, char newmode);
00879 static int set_mode_ic706(struct rpt *myrpt, char newmode);
00880 static int simple_command_ft897(struct rpt *myrpt, char command);
00881 static int setrem(struct rpt *myrpt);
00882 static int setrtx_check(struct rpt *myrpt);
00883 static int channel_revert(struct rpt *myrpt);
00884 static int channel_steer(struct rpt *myrpt, char *data);
00885 
00886 AST_MUTEX_DEFINE_STATIC(nodeloglock);
00887 
00888 AST_MUTEX_DEFINE_STATIC(nodelookuplock);
00889 
00890 #ifdef   APP_RPT_LOCK_DEBUG
00891 
00892 #warning COMPILING WITH LOCK-DEBUGGING ENABLED!!
00893 
00894 #define  MAXLOCKTHREAD 100
00895 
00896 #define rpt_mutex_lock(x) _rpt_mutex_lock(x,myrpt,__LINE__)
00897 #define rpt_mutex_unlock(x) _rpt_mutex_unlock(x,myrpt,__LINE__)
00898 
00899 struct lockthread
00900 {
00901    pthread_t id;
00902    int lockcount;
00903    int lastlock;
00904    int lastunlock;
00905 } lockthreads[MAXLOCKTHREAD];
00906 
00907 
00908 struct by_lightning
00909 {
00910    int line;
00911    struct timeval tv;
00912    struct rpt *rpt;
00913    struct lockthread lockthread;
00914 } lock_ring[32];
00915 
00916 int lock_ring_index = 0;
00917 
00918 AST_MUTEX_DEFINE_STATIC(locklock);
00919 
00920 static struct lockthread *get_lockthread(pthread_t id)
00921 {
00922 int   i;
00923 
00924    for(i = 0; i < MAXLOCKTHREAD; i++)
00925    {
00926       if (lockthreads[i].id == id) return(&lockthreads[i]);
00927    }
00928    return(NULL);
00929 }
00930 
00931 static struct lockthread *put_lockthread(pthread_t id)
00932 {
00933 int   i;
00934 
00935    for(i = 0; i < MAXLOCKTHREAD; i++)
00936    {
00937       if (lockthreads[i].id == id)
00938          return(&lockthreads[i]);
00939    }
00940    for(i = 0; i < MAXLOCKTHREAD; i++)
00941    {
00942       if (!lockthreads[i].id)
00943       {
00944          lockthreads[i].lockcount = 0;
00945          lockthreads[i].lastlock = 0;
00946          lockthreads[i].lastunlock = 0;
00947          lockthreads[i].id = id;
00948          return(&lockthreads[i]);
00949       }
00950    }
00951    return(NULL);
00952 }
00953 
00954 
00955 static void rpt_mutex_spew(void)
00956 {
00957    struct by_lightning lock_ring_copy[32];
00958    int lock_ring_index_copy;
00959    int i,j;
00960    long long diff;
00961    char a[100];
00962    struct timeval lasttv;
00963 
00964    ast_mutex_lock(&locklock);
00965    memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy));
00966    lock_ring_index_copy = lock_ring_index;
00967    ast_mutex_unlock(&locklock);
00968 
00969    lasttv.tv_sec = lasttv.tv_usec = 0;
00970    for(i = 0 ; i < 32 ; i++)
00971    {
00972       j = (i + lock_ring_index_copy) % 32;
00973       strftime(a,sizeof(a) - 1,"%m/%d/%Y %H:%M:%S",
00974          localtime(&lock_ring_copy[j].tv.tv_sec));
00975       diff = 0;
00976       if(lasttv.tv_sec)
00977       {
00978          diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec)
00979             * 1000000;
00980          diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec);
00981       }
00982       lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec;
00983       lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec;
00984       if (!lock_ring_copy[j].tv.tv_sec) continue;
00985       if (lock_ring_copy[j].line < 0)
00986       {
00987          ast_log(LOG_NOTICE,"LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
00988             i - 31,-lock_ring_copy[j].line,lock_ring_copy[j].rpt->name,(int) lock_ring_copy[j].lockthread.id,diff,a,(int)lock_ring_copy[j].tv.tv_usec);
00989       }
00990       else
00991       {
00992          ast_log(LOG_NOTICE,"LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
00993             i - 31,lock_ring_copy[j].line,lock_ring_copy[j].rpt->name,(int) lock_ring_copy[j].lockthread.id,diff,a,(int)lock_ring_copy[j].tv.tv_usec);
00994       }
00995    }
00996 }
00997 
00998 
00999 static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
01000 {
01001 struct lockthread *t;
01002 pthread_t id;
01003 
01004    id = pthread_self();
01005    ast_mutex_lock(&locklock);
01006    t = put_lockthread(id);
01007    if (!t)
01008    {
01009       ast_mutex_unlock(&locklock);
01010       return;
01011    }
01012    if (t->lockcount)
01013    {
01014       int lastline = t->lastlock;
01015       ast_mutex_unlock(&locklock);
01016       ast_log(LOG_NOTICE,"rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",line,myrpt->name,(int) t->id,lastline);
01017       rpt_mutex_spew();
01018       return;
01019    }
01020    t->lastlock = line;
01021    t->lockcount = 1;
01022    gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
01023    lock_ring[lock_ring_index].rpt = myrpt;
01024    memcpy(&lock_ring[lock_ring_index].lockthread,t,sizeof(struct lockthread));
01025    lock_ring[lock_ring_index++].line = line;
01026    if(lock_ring_index == 32)
01027       lock_ring_index = 0;
01028    ast_mutex_unlock(&locklock);
01029    ast_mutex_lock(lockp);
01030 }
01031 
01032 
01033 static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
01034 {
01035 struct lockthread *t;
01036 pthread_t id;
01037 
01038    id = pthread_self();
01039    ast_mutex_lock(&locklock);
01040    t = put_lockthread(id);
01041    if (!t)
01042    {
01043       ast_mutex_unlock(&locklock);
01044       return;
01045    }
01046    if (!t->lockcount)
01047    {
01048       int lastline = t->lastunlock;
01049       ast_mutex_unlock(&locklock);
01050       ast_log(LOG_NOTICE,"rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",line,myrpt->name,(int) t->id,lastline);
01051       rpt_mutex_spew();
01052       return;
01053    }
01054    t->lastunlock = line;
01055    t->lockcount = 0;
01056    gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
01057    lock_ring[lock_ring_index].rpt = myrpt;
01058    memcpy(&lock_ring[lock_ring_index].lockthread,t,sizeof(struct lockthread));
01059    lock_ring[lock_ring_index++].line = -line;
01060    if(lock_ring_index == 32)
01061       lock_ring_index = 0;
01062    ast_mutex_unlock(&locklock);
01063    ast_mutex_unlock(lockp);
01064 }
01065 
01066 #else  /* APP_RPT_LOCK_DEBUG */
01067 
01068 #define rpt_mutex_lock(x) ast_mutex_lock(x)
01069 #define rpt_mutex_unlock(x) ast_mutex_unlock(x)
01070 
01071 #endif  /* APP_RPT_LOCK_DEBUG */
01072 
01073 /*
01074 * Return 1 if rig is multimode capable
01075 */
01076 
01077 static int multimode_capable(struct rpt *myrpt)
01078 {
01079    if(!strcmp(myrpt->remoterig, remote_rig_ft897))
01080       return 1;
01081    if(!strcmp(myrpt->remoterig, remote_rig_ic706))
01082       return 1;
01083    return 0;
01084 }  
01085 
01086 static void voxinit_rpt(struct rpt *myrpt,char enable)
01087 {
01088 
01089    myrpt->vox.speech_energy = 0.0;
01090    myrpt->vox.noise_energy = 0.0;
01091    myrpt->vox.enacount = 0;
01092    myrpt->vox.voxena = 0;
01093    if (!enable) myrpt->vox.voxena = -1;
01094    myrpt->vox.lastvox = 0;
01095    myrpt->vox.ondebcnt = VOX_ON_DEBOUNCE_COUNT;
01096    myrpt->vox.offdebcnt = VOX_OFF_DEBOUNCE_COUNT;
01097    myrpt->wasvox = 0;
01098    myrpt->voxtotimer = 0;
01099    myrpt->voxtostate = 0;
01100 }
01101 
01102 static void voxinit_link(struct rpt_link *mylink,char enable)
01103 {
01104 
01105    mylink->vox.speech_energy = 0.0;
01106    mylink->vox.noise_energy = 0.0;
01107    mylink->vox.enacount = 0;
01108    mylink->vox.voxena = 0;
01109    if (!enable) mylink->vox.voxena = -1;
01110    mylink->vox.lastvox = 0;
01111    mylink->vox.ondebcnt = VOX_ON_DEBOUNCE_COUNT;
01112    mylink->vox.offdebcnt = VOX_OFF_DEBOUNCE_COUNT;
01113    mylink->wasvox = 0;
01114    mylink->voxtotimer = 0;
01115    mylink->voxtostate = 0;
01116 }
01117 
01118 static int dovox(struct vox *v,short *buf,int bs)
01119 {
01120 
01121    int i;
01122    float esquare = 0.0;
01123    float energy = 0.0;
01124    float threshold = 0.0;
01125    
01126    if (v->voxena < 0) return(v->lastvox);
01127    for(i = 0; i < bs; i++)
01128    {
01129       esquare += (float) buf[i] * (float) buf[i];
01130    }
01131    energy = sqrt(esquare);
01132 
01133    if (energy >= v->speech_energy)
01134       v->speech_energy += (energy - v->speech_energy) / 4;
01135    else
01136       v->speech_energy += (energy - v->speech_energy) / 64;
01137 
01138    if (energy >= v->noise_energy)
01139       v->noise_energy += (energy - v->noise_energy) / 64;
01140    else
01141       v->noise_energy += (energy - v->noise_energy) / 4;
01142    
01143    if (v->voxena) threshold = v->speech_energy / 8;
01144    else
01145    {
01146       threshold = mymax(v->speech_energy / 16,v->noise_energy * 2);
01147       threshold = mymin(threshold,VOX_MAX_THRESHOLD);
01148    }
01149    threshold = mymax(threshold,VOX_MIN_THRESHOLD);
01150    if (energy > threshold)
01151    {
01152       if (v->voxena) v->noise_energy *= 0.75;
01153       v->voxena = 1;
01154    } else   v->voxena = 0;
01155    if (v->lastvox != v->voxena)
01156    {
01157       if (v->enacount++ >= ((v->lastvox) ? v->offdebcnt : v->ondebcnt))
01158       {
01159          v->lastvox = v->voxena;
01160          v->enacount = 0;
01161       }
01162    } else v->enacount = 0;
01163    return(v->lastvox);
01164 }
01165 
01166 
01167 
01168 
01169 /*
01170 * CLI extensions
01171 */
01172 
01173 /* Debug mode */
01174 static int rpt_do_debug(int fd, int argc, char *argv[]);
01175 static int rpt_do_dump(int fd, int argc, char *argv[]);
01176 static int rpt_do_stats(int fd, int argc, char *argv[]);
01177 static int rpt_do_lstats(int fd, int argc, char *argv[]);
01178 static int rpt_do_nodes(int fd, int argc, char *argv[]);
01179 static int rpt_do_local_nodes(int fd, int argc, char *argv[]);
01180 static int rpt_do_reload(int fd, int argc, char *argv[]);
01181 static int rpt_do_restart(int fd, int argc, char *argv[]);
01182 static int rpt_do_fun(int fd, int argc, char *argv[]);
01183 static int rpt_do_fun1(int fd, int argc, char *argv[]);
01184 static int rpt_do_cmd(int fd, int argc, char *argv[]);
01185 
01186 static char debug_usage[] =
01187 "Usage: rpt debug level {0-7}\n"
01188 "       Enables debug messages in app_rpt\n";
01189 
01190 static char dump_usage[] =
01191 "Usage: rpt dump <nodename>\n"
01192 "       Dumps struct debug info to log\n";
01193 
01194 static char dump_stats[] =
01195 "Usage: rpt stats <nodename>\n"
01196 "       Dumps node statistics to console\n";
01197 
01198 static char dump_lstats[] =
01199 "Usage: rpt lstats <nodename>\n"
01200 "       Dumps link statistics to console\n";
01201 
01202 static char dump_nodes[] =
01203 "Usage: rpt nodes <nodename>\n"
01204 "       Dumps a list of directly and indirectly connected nodes to the console\n";
01205 
01206 static char usage_local_nodes[] =
01207 "Usage: rpt localnodes\n"
01208 "       Dumps a list of the locally configured node numbers to the console.\n";
01209 
01210 static char reload_usage[] =
01211 "Usage: rpt reload\n"
01212 "       Reloads app_rpt running config parameters\n";
01213 
01214 static char restart_usage[] =
01215 "Usage: rpt restart\n"
01216 "       Restarts app_rpt\n";
01217 
01218 static char fun_usage[] =
01219 "Usage: rpt fun <nodename> <command>\n"
01220 "       Send a DTMF function to a node\n";
01221 
01222 static char cmd_usage[] =
01223 "Usage: rpt cmd <nodename> <cmd-name> <cmd-index> <cmd-args.\n"
01224 "       Send a command to a node.\n        i.e. rpt cmd 2000 ilink 3 2001\n";
01225 
01226 #ifndef  NEW_ASTERISK
01227 
01228 static struct ast_cli_entry  cli_debug =
01229         { { "rpt", "debug", "level" }, rpt_do_debug, 
01230       "Enable app_rpt debugging", debug_usage };
01231 
01232 static struct ast_cli_entry  cli_dump =
01233         { { "rpt", "dump" }, rpt_do_dump,
01234       "Dump app_rpt structs for debugging", dump_usage };
01235 
01236 static struct ast_cli_entry  cli_stats =
01237         { { "rpt", "stats" }, rpt_do_stats,
01238       "Dump node statistics", dump_stats };
01239 
01240 static struct ast_cli_entry  cli_nodes =
01241         { { "rpt", "nodes" }, rpt_do_nodes,
01242       "Dump node list", dump_nodes };
01243 
01244 static struct ast_cli_entry  cli_local_nodes =
01245         { { "rpt", "localnodes" }, rpt_do_local_nodes,
01246       "Dump list of local node numbers", usage_local_nodes };
01247 
01248 static struct ast_cli_entry  cli_lstats =
01249         { { "rpt", "lstats" }, rpt_do_lstats,
01250       "Dump link statistics", dump_lstats };
01251 
01252 static struct ast_cli_entry  cli_reload =
01253         { { "rpt", "reload" }, rpt_do_reload,
01254       "Reload app_rpt config", reload_usage };
01255 
01256 static struct ast_cli_entry  cli_restart =
01257         { { "rpt", "restart" }, rpt_do_restart,
01258       "Restart app_rpt", restart_usage };
01259 
01260 static struct ast_cli_entry  cli_fun =
01261         { { "rpt", "fun" }, rpt_do_fun,
01262       "Execute a DTMF function", fun_usage };
01263 
01264 static struct ast_cli_entry  cli_fun1 =
01265         { { "rpt", "fun1" }, rpt_do_fun1,
01266       "Execute a DTMF function", fun_usage };
01267 
01268 static struct ast_cli_entry  cli_cmd =
01269         { { "rpt", "cmd" }, rpt_do_cmd,
01270       "Execute a DTMF function", cmd_usage };
01271 
01272 #endif
01273 
01274 /*
01275 * Telemetry defaults
01276 */
01277 
01278 
01279 static struct telem_defaults tele_defs[] = {
01280    {"ct1","|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
01281    {"ct2","|t(660,880,150,3072)"},
01282    {"ct3","|t(440,0,150,3072)"},
01283    {"ct4","|t(550,0,150,3072)"},
01284    {"ct5","|t(660,0,150,3072)"},
01285    {"ct6","|t(880,0,150,3072)"},
01286    {"ct7","|t(660,440,150,3072)"},
01287    {"ct8","|t(700,1100,150,3072)"},
01288    {"remotemon","|t(1600,0,75,2048)"},
01289    {"remotetx","|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
01290    {"cmdmode","|t(900,904,200,2048)"},
01291    {"functcomplete","|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
01292 } ;
01293 
01294 /*
01295 * Forward decl's - these suppress compiler warnings when funcs coded further down the file than thier invokation
01296 */
01297 
01298 static int setrbi(struct rpt *myrpt);
01299 static int set_ft897(struct rpt *myrpt);
01300 static int set_ic706(struct rpt *myrpt);
01301 static int setkenwood(struct rpt *myrpt);
01302 static int set_tm271(struct rpt *myrpt);
01303 static int setrbi_check(struct rpt *myrpt);
01304 
01305 
01306 
01307 /*
01308 * Define function protos for function table here
01309 */
01310 
01311 static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01312 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01313 static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01314 static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01315 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01316 static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01317 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01318 static int function_playback(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
01319 /*
01320 * Function table
01321 */
01322 
01323 static struct function_table_tag function_table[] = {
01324    {"cop", function_cop},
01325    {"autopatchup", function_autopatchup},
01326    {"autopatchdn", function_autopatchdn},
01327    {"ilink", function_ilink},
01328    {"status", function_status},
01329    {"remote", function_remote},
01330    {"macro", function_macro},
01331    {"playback", function_playback}
01332 } ;
01333 
01334 static long diskavail(struct rpt *myrpt)
01335 {
01336 struct   statfs statfsbuf;
01337 
01338    if (!myrpt->p.archivedir) return(0);
01339    if (statfs(myrpt->p.archivedir,&statfsbuf) == -1)
01340    {
01341       ast_log(LOG_WARNING,"Cannot get filesystem size for %s node %s\n",
01342          myrpt->p.archivedir,myrpt->name);
01343       return(-1);
01344    }
01345    return(statfsbuf.f_bavail);
01346 }
01347 
01348 static void flush_telem(struct rpt *myrpt)
01349 {
01350    struct rpt_tele *telem;
01351    if(debug > 2)
01352       ast_log(LOG_NOTICE, "flush_telem()!!");
01353    rpt_mutex_lock(&myrpt->lock);
01354    telem = myrpt->tele.next;
01355    while(telem != &myrpt->tele)
01356    {
01357       if (telem->mode != SETREMOTE) ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
01358       telem = telem->next;
01359    }
01360    rpt_mutex_unlock(&myrpt->lock);
01361 }
01362 /*
01363    return via error priority
01364 */
01365 static int priority_jump(struct rpt *myrpt, struct ast_channel *chan)
01366 {
01367    int res=0;
01368 
01369    // if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0){
01370    if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0){
01371       res = 0;
01372    } else {
01373       res = -1;
01374    }
01375    return res;
01376 }
01377 /*
01378 */
01379 static int linkcount(struct rpt *myrpt)
01380 {
01381    struct   rpt_link *l;
01382    char *reverse_patch_state;
01383    int numoflinks;
01384 
01385    reverse_patch_state = "DOWN";
01386    numoflinks = 0;
01387    l = myrpt->links.next;
01388    while(l && (l != &myrpt->links)){
01389       if(numoflinks >= MAX_STAT_LINKS){
01390          ast_log(LOG_WARNING,
01391          "maximum number of links exceeds %d in rpt_do_stats()!",MAX_STAT_LINKS);
01392          break;
01393       }
01394       //if (l->name[0] == '0'){ /* Skip '0' nodes */
01395       // reverse_patch_state = "UP";
01396       // l = l->next;
01397       // continue;
01398       //}
01399       numoflinks++;
01400     
01401       l = l->next;
01402    }
01403    ast_log(LOG_NOTICE, "numoflinks=%i\n",numoflinks);
01404    return numoflinks;
01405 }
01406 /*
01407  * Retrieve a memory channel
01408  * Return 0 if sucessful,
01409  * -1 if channel not found,
01410  *  1 if parse error
01411  */
01412 static int retreive_memory(struct rpt *myrpt, char *memory)
01413 {
01414    char tmp[30], *s, *s1, *val;
01415 
01416    if (debug)ast_log(LOG_NOTICE, "memory=%s block=%s\n",memory,myrpt->p.memory);
01417 
01418    val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, memory);
01419    if (!val){
01420       return -1;
01421    }        
01422    strncpy(tmp,val,sizeof(tmp) - 1);
01423    tmp[sizeof(tmp)-1] = 0;
01424 
01425    s = strchr(tmp,',');
01426    if (!s)
01427       return 1; 
01428    *s++ = 0;
01429    s1 = strchr(s,',');
01430    if (!s1)
01431       return 1;
01432    *s1++ = 0;
01433    strncpy(myrpt->freq, tmp, sizeof(myrpt->freq) - 1);
01434    strncpy(myrpt->rxpl, s, sizeof(myrpt->rxpl) - 1);
01435    strncpy(myrpt->txpl, s, sizeof(myrpt->rxpl) - 1);
01436    myrpt->remmode = REM_MODE_FM;
01437    myrpt->offset = REM_SIMPLEX;
01438    myrpt->powerlevel = REM_MEDPWR;
01439    myrpt->txplon = myrpt->rxplon = 0;
01440    while(*s1){
01441       switch(*s1++){
01442          case 'A':
01443          case 'a':
01444             strcpy(myrpt->rxpl, "100.0");
01445             strcpy(myrpt->txpl, "100.0");
01446             myrpt->remmode = REM_MODE_AM; 
01447             break;
01448          case 'B':
01449          case 'b':
01450             strcpy(myrpt->rxpl, "100.0");
01451             strcpy(myrpt->txpl, "100.0");
01452             myrpt->remmode = REM_MODE_LSB;
01453             break;
01454          case 'F':
01455             myrpt->remmode = REM_MODE_FM;
01456             break;
01457          case 'L':
01458          case 'l':
01459             myrpt->powerlevel = REM_LOWPWR;
01460             break;               
01461          case 'H':
01462          case 'h':
01463             myrpt->powerlevel = REM_HIPWR;
01464             break;
01465                
01466          case 'M':
01467          case 'm':
01468             myrpt->powerlevel = REM_MEDPWR;
01469             break;
01470                   
01471          case '-':
01472             myrpt->offset = REM_MINUS;
01473             break;
01474                   
01475          case '+':
01476             myrpt->offset = REM_PLUS;
01477             break;
01478                   
01479          case 'S':
01480          case 's':
01481             myrpt->offset = REM_SIMPLEX;
01482             break;
01483                   
01484          case 'T':
01485          case 't':
01486             myrpt->txplon = 1;
01487             break;
01488                   
01489          case 'R':
01490          case 'r':
01491             myrpt->rxplon = 1;
01492             break;
01493 
01494          case 'U':
01495          case 'u':
01496             strcpy(myrpt->rxpl, "100.0");
01497             strcpy(myrpt->txpl, "100.0");
01498             myrpt->remmode = REM_MODE_USB;
01499             break;
01500          default:
01501             return 1;
01502       }
01503    }
01504    return 0;
01505 }
01506 /*
01507 
01508 */
01509 static void birdbath(struct rpt *myrpt)
01510 {
01511    struct rpt_tele *telem;
01512    if(debug > 2)
01513       ast_log(LOG_NOTICE, "birdbath!!");
01514    rpt_mutex_lock(&myrpt->lock);
01515    telem = myrpt->tele.next;
01516    while(telem != &myrpt->tele)
01517    {
01518       if (telem->mode == PARROT) ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
01519       telem = telem->next;
01520    }
01521    rpt_mutex_unlock(&myrpt->lock);
01522 }
01523 
01524 static void do_dtmf_phone(struct rpt *myrpt, struct rpt_link *mylink, char c)
01525 {
01526 struct        rpt_link *l;
01527 
01528        l = myrpt->links.next;
01529        /* go thru all the links */
01530        while(l != &myrpt->links)
01531        {
01532                if (!l->phonemode)
01533                {
01534                        l = l->next;
01535                        continue;
01536                }
01537                /* dont send to self */
01538                if (mylink && (l == mylink))
01539                {
01540                        l = l->next;
01541                        continue;
01542                }
01543 #ifdef   NEW_ASTERISK
01544                if (l->chan) ast_senddigit(l->chan,c,0);
01545 #else
01546                if (l->chan) ast_senddigit(l->chan,c);
01547 #endif
01548                l = l->next;
01549        }
01550        return;
01551 }
01552 
01553 /* node logging function */
01554 static void donodelog(struct rpt *myrpt,char *str)
01555 {
01556 struct nodelog *nodep;
01557 char  datestr[100];
01558 
01559    if (!myrpt->p.archivedir) return;
01560    nodep = (struct nodelog *)ast_malloc(sizeof(struct nodelog));
01561    if (nodep == NULL)
01562    {
01563       ast_log(LOG_ERROR,"Cannot get memory for node log");
01564       return;
01565    }
01566    time(&nodep->timestamp);
01567    strncpy(nodep->archivedir,myrpt->p.archivedir,
01568       sizeof(nodep->archivedir) - 1);
01569    strftime(datestr,sizeof(datestr) - 1,"%Y%m%d%H%M%S",
01570       localtime(&nodep->timestamp));
01571    snprintf(nodep->str,sizeof(nodep->str) - 1,"%s %s,%s\n",
01572       myrpt->name,datestr,str);
01573    ast_mutex_lock(&nodeloglock);
01574    insque((struct qelem *) nodep, (struct qelem *) nodelog.prev);
01575    ast_mutex_unlock(&nodeloglock);
01576 }
01577 
01578 /* must be called locked */
01579 static void do_dtmf_local(struct rpt *myrpt, char c)
01580 {
01581 int   i;
01582 char  digit;
01583 static const char* dtmf_tones[] = {
01584    "!941+1336/200,!0/200", /* 0 */
01585    "!697+1209/200,!0/200", /* 1 */
01586    "!697+1336/200,!0/200", /* 2 */
01587    "!697+1477/200,!0/200", /* 3 */
01588    "!770+1209/200,!0/200", /* 4 */
01589    "!770+1336/200,!0/200", /* 5 */
01590    "!770+1477/200,!0/200", /* 6 */
01591    "!852+1209/200,!0/200", /* 7 */
01592    "!852+1336/200,!0/200", /* 8 */
01593    "!852+1477/200,!0/200", /* 9 */
01594    "!697+1633/200,!0/200", /* A */
01595    "!770+1633/200,!0/200", /* B */
01596    "!852+1633/200,!0/200", /* C */
01597    "!941+1633/200,!0/200", /* D */
01598    "!941+1209/200,!0/200", /* * */
01599    "!941+1477/200,!0/200" };  /* # */
01600 
01601 
01602    if (c)
01603    {
01604       snprintf(myrpt->dtmf_local_str + strlen(myrpt->dtmf_local_str),sizeof(myrpt->dtmf_local_str) - 1,"%c",c);
01605       if (!myrpt->dtmf_local_timer) 
01606           myrpt->dtmf_local_timer = DTMF_LOCAL_STARTTIME;
01607    }
01608    /* if at timeout */
01609    if (myrpt->dtmf_local_timer == 1)
01610    {
01611       if(debug > 6)
01612          ast_log(LOG_NOTICE,"time out dtmf_local_timer=%i\n",myrpt->dtmf_local_timer);
01613 
01614       /* if anything in the string */
01615       if (myrpt->dtmf_local_str[0])
01616       {
01617          digit = myrpt->dtmf_local_str[0];
01618          myrpt->dtmf_local_str[0] = 0;
01619          for(i = 1; myrpt->dtmf_local_str[i]; i++)
01620          {
01621             myrpt->dtmf_local_str[i - 1] =
01622                myrpt->dtmf_local_str[i];
01623          }
01624          myrpt->dtmf_local_str[i - 1] = 0;
01625          myrpt->dtmf_local_timer = DTMF_LOCAL_TIME;
01626          rpt_mutex_unlock(&myrpt->lock);
01627          if (digit >= '0' && digit <='9')
01628             ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[digit-'0'], 0);
01629          else if (digit >= 'A' && digit <= 'D')
01630             ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[digit-'A'+10], 0);
01631          else if (digit == '*')
01632             ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[14], 0);
01633          else if (digit == '#')
01634             ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[15], 0);
01635          else {
01636             /* not handled */
01637             ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, myrpt->txchannel->name);
01638          }
01639          rpt_mutex_lock(&myrpt->lock);
01640       }
01641       else
01642       {
01643          myrpt->dtmf_local_timer = 0;
01644       }
01645    }
01646 }
01647 
01648 static int setdtr(int fd, int enable)
01649 {
01650 struct termios mode;
01651 
01652    if (fd < 0) return -1;
01653    if (tcgetattr(fd, &mode)) {
01654       ast_log(LOG_WARNING, "Unable to get serial parameters for dtr: %s\n", strerror(errno));
01655       return -1;
01656    }
01657    if (enable)
01658    {
01659       cfsetspeed(&mode, B9600);
01660    }
01661    else
01662    {
01663       cfsetspeed(&mode, B0);
01664       usleep(100000);
01665    }
01666    if (tcsetattr(fd, TCSADRAIN, &mode)) {
01667       ast_log(LOG_WARNING, "Unable to set serial parameters for dtr: %s\n", strerror(errno));
01668       return -1;
01669    }
01670    if (enable) usleep(100000);
01671    return 0;
01672 }
01673 
01674 static int openserial(struct rpt *myrpt,char *fname)
01675 {
01676    struct termios mode;
01677    int fd;
01678 
01679    fd = open(fname,O_RDWR);
01680    if (fd == -1)
01681    {
01682       ast_log(LOG_WARNING,"Cannot open serial port %s\n",fname);
01683       return -1;
01684    }
01685    memset(&mode, 0, sizeof(mode));
01686    if (tcgetattr(fd, &mode)) {
01687       ast_log(LOG_WARNING, "Unable to get serial parameters on %s: %s\n", fname, strerror(errno));
01688       return -1;
01689    }
01690 #ifndef  SOLARIS
01691    cfmakeraw(&mode);
01692 #else
01693         mode.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
01694                         |INLCR|IGNCR|ICRNL|IXON);
01695         mode.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
01696         mode.c_cflag &= ~(CSIZE|PARENB|CRTSCTS);
01697         mode.c_cflag |= CS8;
01698    mode.c_cc[VTIME] = 3;
01699    mode.c_cc[VMIN] = 1; 
01700 #endif
01701 
01702    cfsetispeed(&mode, B9600);
01703    cfsetospeed(&mode, B9600);
01704    if (tcsetattr(fd, TCSANOW, &mode)) 
01705       ast_log(LOG_WARNING, "Unable to set serial parameters on %s: %s\n", fname, strerror(errno));
01706    if(!strcmp(myrpt->remoterig, remote_rig_kenwood)) setdtr(fd,0); 
01707    usleep(100000);
01708    if (debug)ast_log(LOG_NOTICE,"Opened serial port %s\n",fname);
01709    return(fd); 
01710 }
01711 
01712 static void mdc1200_notify(struct rpt *myrpt,char *fromnode, unsigned int unit)
01713 {
01714    if (!fromnode)
01715    {
01716       ast_verbose("Got MDC-1200 ID %04X from local system (%s)\n",
01717          unit,myrpt->name);
01718    }
01719    else
01720    {
01721       ast_verbose("Got MDC-1200 ID %04X from node %s (%s)\n",
01722          unit,fromnode,myrpt->name);
01723    }
01724 }
01725 
01726 #ifdef   _MDC_DECODE_H_
01727 
01728 static void mdc1200_send(struct rpt *myrpt, unsigned int unit)
01729 {
01730 struct rpt_link *l;
01731 struct   ast_frame wf;
01732 char  str[200];
01733 
01734 
01735    sprintf(str,"I %s %04X",myrpt->name,unit);
01736 
01737    wf.frametype = AST_FRAME_TEXT;
01738    wf.subclass = 0;
01739    wf.offset = 0;
01740    wf.mallocd = 0;
01741    wf.datalen = strlen(str) + 1;
01742    wf.samples = 0;
01743 
01744 
01745    l = myrpt->links.next;
01746    /* otherwise, send it to all of em */
01747    while(l != &myrpt->links)
01748    {
01749       if (l->name[0] == '0') 
01750       {
01751          l = l->next;
01752          continue;
01753       }
01754       wf.data = str;
01755       if (l->chan) ast_write(l->chan,&wf); 
01756       l = l->next;
01757    }
01758    return;
01759 }
01760 
01761 #endif
01762 
01763 static char func_xlat(struct rpt *myrpt,char c,struct rpt_xlat *xlat)
01764 {
01765 time_t   now;
01766 int   gotone;
01767 
01768    time(&now);
01769    gotone = 0;
01770    /* if too much time, reset the skate machine */
01771    if ((now - xlat->lastone) > MAXXLATTIME)
01772    {
01773       xlat->funcindex = xlat->endindex = 0;
01774    }
01775    if (xlat->funccharseq[0] && (c == xlat->funccharseq[xlat->funcindex++]))
01776    {
01777       time(&xlat->lastone);
01778       gotone = 1;
01779       if (!xlat->funccharseq[xlat->funcindex])
01780       {
01781          xlat->funcindex = xlat->endindex = 0;
01782          return(myrpt->p.funcchar);
01783       }
01784    } else xlat->funcindex = 0;
01785    if (xlat->endcharseq[0] && (c == xlat->endcharseq[xlat->endindex++]))
01786    {
01787       time(&xlat->lastone);
01788       gotone = 1;
01789       if (!xlat->endcharseq[xlat->endindex])
01790       {
01791          xlat->funcindex = xlat->endindex = 0;
01792          return(myrpt->p.endchar);
01793       }
01794    } else xlat->endindex = 0;
01795    /* if in middle of decode seq, send nothing back */
01796    if (gotone) return(0);
01797    /* if no pass chars specified, return em all */
01798    if (!xlat->passchars[0]) return(c);
01799    /* if a "pass char", pass it */
01800    if (strchr(xlat->passchars,c)) return(c);
01801    return(0);
01802 }
01803 
01804 /*
01805  * Return a pointer to the first non-whitespace character
01806  */
01807 
01808 static char *eatwhite(char *s)
01809 {
01810    while((*s == ' ') || (*s == 0x09)){ /* get rid of any leading white space */
01811       if(!*s)
01812          break;
01813       s++;
01814    }
01815    return s;
01816 }
01817 
01818 /*
01819 * Break up a delimited string into a table of substrings
01820 *
01821 * str - delimited string ( will be modified )
01822 * strp- list of pointers to substrings (this is built by this function), NULL will be placed at end of list
01823 * limit- maximum number of substrings to process
01824 */
01825    
01826 
01827 
01828 static int finddelim(char *str, char *strp[], int limit)
01829 {
01830 int     i,l,inquo;
01831 
01832         inquo = 0;
01833         i = 0;
01834         strp[i++] = str;
01835         if (!*str)
01836            {
01837                 strp[0] = 0;
01838                 return(0);
01839            }
01840         for(l = 0; *str && (l < limit) ; str++)
01841            {
01842                 if (*str == QUOTECHR)
01843                    {
01844                         if (inquo)
01845                            {
01846                                 *str = 0;
01847                                 inquo = 0;
01848                            }
01849                         else
01850                            {
01851                                 strp[i - 1] = str + 1;
01852                                 inquo = 1;
01853                            }
01854       }
01855                 if ((*str == DELIMCHR) && (!inquo))
01856                 {
01857                         *str = 0;
01858          l++;
01859                         strp[i++] = str + 1;
01860                 }
01861            }
01862         strp[i] = 0;
01863         return(i);
01864 
01865 }
01866 /*
01867    send asterisk frame text message on the current tx channel
01868 */
01869 static int send_usb_txt(struct rpt *myrpt, char *txt) 
01870 {
01871    struct ast_frame wf;
01872  
01873    if (debug)ast_log(LOG_NOTICE, "send_usb_txt %s\n",txt);
01874    wf.frametype = AST_FRAME_TEXT;
01875    wf.subclass = 0;
01876    wf.offset = 0;
01877    wf.mallocd = 0;
01878    wf.datalen = strlen(txt) + 1;
01879    wf.data.ptr = txt;
01880    wf.samples = 0;
01881    ast_write(myrpt->txchannel,&wf); 
01882    return 0;
01883 }
01884 /* must be called locked */
01885 static void __mklinklist(struct rpt *myrpt, struct rpt_link *mylink, char *buf)
01886 {
01887 struct rpt_link *l;
01888 char mode;
01889 int   i,spos;
01890 
01891    buf[0] = 0; /* clear output buffer */
01892    if (myrpt->remote) return;
01893    /* go thru all links */
01894    for(l = myrpt->links.next; l != &myrpt->links; l = l->next)
01895    {
01896       /* if is not a real link, ignore it */
01897       if (l->name[0] == '0') continue;
01898       /* dont count our stuff */
01899       if (l == mylink) continue;
01900       if (mylink && (!strcmp(l->name,mylink->name))) continue;
01901       /* figure out mode to report */
01902       mode = 'T'; /* use Tranceive by default */
01903       if (!l->mode) mode = 'R'; /* indicate RX for our mode */
01904       if (!l->thisconnected)  mode = 'C'; /* indicate connecting */
01905       spos = strlen(buf); /* current buf size (b4 we add our stuff) */
01906       if (spos)
01907       {
01908          strcat(buf,",");
01909          spos++;
01910       }
01911       /* add nodes into buffer */
01912       if (l->linklist[0])
01913       {
01914          snprintf(buf + spos,MAXLINKLIST - spos,
01915             "%c%s,%s",mode,l->name,l->linklist);
01916       }
01917       else /* if no nodes, add this node into buffer */
01918       {
01919          snprintf(buf + spos,MAXLINKLIST - spos,
01920             "%c%s",mode,l->name);
01921       }
01922       /* if we are in tranceive mode, let all modes stand */
01923       if (mode == 'T') continue;
01924       /* downgrade everyone on this node if appropriate */
01925       for(i = spos; buf[i]; i++)
01926       {
01927          if (buf[i] == 'T') buf[i] = mode;
01928          if ((buf[i] == 'R') && (mode == 'C')) buf[i] = mode;
01929       }
01930    }
01931    return;
01932 }
01933 
01934 /* must be called locked */
01935 static void __kickshort(struct rpt *myrpt)
01936 {
01937 struct rpt_link *l;
01938 
01939    for(l = myrpt->links.next; l != &myrpt->links; l = l->next)
01940    {
01941       /* if is not a real link, ignore it */
01942       if (l->name[0] == '0') continue;
01943       l->linklisttimer = LINKLISTSHORTTIME;
01944    }
01945    myrpt->linkposttimer = LINKPOSTSHORTTIME;
01946    return;
01947 }
01948 
01949 static void statpost(struct rpt *myrpt,char *pairs)
01950 {
01951 char *str,*astr;
01952 char *astrs[100];
01953 int   n,pid;
01954 time_t   now;
01955 unsigned int seq;
01956 
01957    if (!myrpt->p.statpost_url) return;
01958    str = ast_malloc(strlen(pairs) + strlen(myrpt->p.statpost_url) + 200);
01959    astr = ast_strdup(myrpt->p.statpost_program);
01960    if ((!str) || (!astr)) return;
01961    n = finddelim(astr,astrs,100);
01962    if (n < 1) return;
01963    ast_mutex_lock(&myrpt->statpost_lock);
01964    seq = ++myrpt->statpost_seqno;
01965    ast_mutex_unlock(&myrpt->statpost_lock);
01966    astrs[n++] = str;
01967    astrs[n] = NULL;
01968    time(&now);
01969    sprintf(str,"%s?node=%s&time=%u&seqno=%u",myrpt->p.statpost_url,
01970       myrpt->name,(unsigned int) now,seq);
01971    if (pairs) sprintf(str + strlen(str),"&%s",pairs);
01972    if (!(pid = ast_safe_fork(0)))
01973    {
01974       execv(astrs[0],astrs);
01975       ast_log(LOG_ERROR, "exec of %s failed.\n", astrs[0]);
01976       perror("asterisk");
01977       exit(0);
01978    }
01979    ast_free(astr);
01980    ast_free(str);
01981    return;
01982 }
01983 
01984 static char *node_lookup(struct rpt *myrpt,char *digitbuf)
01985 {
01986 
01987 char *val;
01988 int longestnode,j;
01989 struct stat mystat;
01990 static time_t last = 0;
01991 static struct ast_config *ourcfg = NULL;
01992 struct ast_variable *vp;
01993 
01994    /* try to look it up locally first */
01995    val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
01996    if (val) return(val);
01997    ast_mutex_lock(&nodelookuplock);
01998    /* if file does not exist */
01999    if (stat(myrpt->p.extnodefile,&mystat) == -1)
02000    {
02001       if (ourcfg) ast_config_destroy(ourcfg);
02002       ourcfg = NULL;
02003       ast_mutex_unlock(&nodelookuplock);
02004       return(NULL);
02005    }
02006    /* if we need to reload */
02007    if (mystat.st_mtime > last)
02008    {
02009       if (ourcfg) ast_config_destroy(ourcfg);
02010 #ifdef   NEW_ASTERISK
02011       ourcfg = ast_config_load(myrpt->p.extnodefile,config_flags);
02012 #else
02013       ourcfg = ast_config_load(myrpt->p.extnodefile);
02014 #endif
02015       /* if file not there, just bail */
02016       if (!ourcfg || ourcfg == CONFIG_STATUS_FILEINVALID)
02017       {
02018          ast_mutex_unlock(&nodelookuplock);
02019          return(NULL);
02020       }
02021       /* reset "last" time */
02022       last = mystat.st_mtime;
02023 
02024       /* determine longest node length again */    
02025       longestnode = 0;
02026       vp = ast_variable_browse(myrpt->cfg, myrpt->p.nodes);
02027       while(vp){
02028          j = strlen(vp->name);
02029          if (j > longestnode)
02030             longestnode = j;
02031          vp = vp->next;
02032       }
02033 
02034       vp = ast_variable_browse(ourcfg, myrpt->p.extnodes);
02035       while(vp){
02036          j = strlen(vp->name);
02037          if (j > longestnode)
02038             longestnode = j;
02039          vp = vp->next;
02040       }
02041 
02042       myrpt->longestnode = longestnode;
02043    }
02044    val = NULL;
02045    if (ourcfg)
02046       val = (char *) ast_variable_retrieve(ourcfg, myrpt->p.extnodes, digitbuf);
02047    ast_mutex_unlock(&nodelookuplock);
02048    return(val);
02049 }
02050 
02051 /*
02052 * Match a keyword in a list, and return index of string plus 1 if there was a match,* else return 0.
02053 * If param is passed in non-null, then it will be set to the first character past the match
02054 */
02055 
02056 static int matchkeyword(char *string, char **param, char *keywords[])
02057 {
02058 int   i,ls;
02059    for( i = 0 ; keywords[i] ; i++){
02060       ls = strlen(keywords[i]);
02061       if(!ls){
02062          *param = NULL;
02063          return 0;
02064       }
02065       if(!strncmp(string, keywords[i], ls)){
02066          if(param)
02067             *param = string + ls;
02068          return i + 1; 
02069       }
02070    }
02071    *param = NULL;
02072    return 0;
02073 }
02074 
02075 /*
02076 * Skip characters in string which are in charlist, and return a pointer to the
02077 * first non-matching character
02078 */
02079 
02080 static char *skipchars(char *string, char *charlist)
02081 {
02082 int i;   
02083    while(*string){
02084       for(i = 0; charlist[i] ; i++){
02085          if(*string == charlist[i]){
02086             string++;
02087             break;
02088          }
02089       }
02090       if(!charlist[i])
02091          return string;
02092    }
02093    return string;
02094 }  
02095                
02096 
02097 
02098 static int myatoi(char *str)
02099 {
02100 int   ret;
02101 
02102    if (str == NULL) return -1;
02103    /* leave this %i alone, non-base-10 input is useful here */
02104    if (sscanf(str, "%30i", &ret) != 1) {
02105       return -1;
02106    }
02107 
02108    return ret;
02109 }
02110 
02111 static int mycompar(const void *a, const void *b)
02112 {
02113 char  **x = (char **) a;
02114 char  **y = (char **) b;
02115 int   xoff,yoff;
02116 
02117    if ((**x < '0') || (**x > '9')) xoff = 1; else xoff = 0;
02118    if ((**y < '0') || (**y > '9')) yoff = 1; else yoff = 0;
02119    return(strcmp((*x) + xoff,(*y) + yoff));
02120 }
02121 
02122 static int topcompar(const void *a, const void *b)
02123 {
02124 struct rpt_topkey *x = (struct rpt_topkey *) a;
02125 struct rpt_topkey *y = (struct rpt_topkey *) b;
02126 
02127    return(x->timesince - y->timesince);
02128 }
02129 
02130 #ifdef   __RPT_NOTCH
02131 
02132 /* rpt filter routine */
02133 static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
02134 {
02135 int   i,j;
02136 struct   rptfilter *f;
02137 
02138    for(i = 0; i < len; i++)
02139    {
02140       for(j = 0; j < MAXFILTERS; j++)
02141       {
02142          f = &myrpt->filters[j];
02143          if (!*f->desc) continue;
02144          f->x0 = f->x1; f->x1 = f->x2;
02145               f->x2 = ((float)buf[i]) / f->gain;
02146               f->y0 = f->y1; f->y1 = f->y2;
02147               f->y2 =   (f->x0 + f->x2) +   f->const0 * f->x1
02148                            + (f->const1 * f->y0) + (f->const2 * f->y1);
02149          buf[i] = (short)f->y2;
02150       }
02151    }
02152 }
02153 
02154 #endif
02155 
02156 
02157 /*
02158  Get the time for the machine's time zone
02159  Note: Asterisk requires a copy of localtime
02160  in the /etc directory for this to work properly.
02161  If /etc/localtime is not present, you will get
02162  GMT time! This is especially important on systems
02163  running embedded linux distributions as they don't usually
02164  have support for locales. 
02165 
02166  If OLD_ASTERISK is defined, then the older localtime_r
02167  function will be used. The /etc/localtime file is not
02168  required in this case. This provides backward compatibility
02169  with Asterisk 1.2 systems.
02170 
02171 */
02172 
02173 #ifdef   NEW_ASTERISK
02174 static void rpt_localtime( time_t * t, struct ast_tm *lt)
02175 {
02176    struct timeval when;
02177 
02178    when.tv_sec = *t;
02179    when.tv_usec = 0;
02180    ast_localtime(&when, lt, NULL);
02181 }
02182 
02183 #else
02184 static void rpt_localtime( time_t * t, struct tm *lt)
02185 {
02186 #ifdef OLD_ASTERISK
02187    localtime_r(t, lt);
02188 #else
02189    ast_localtime(t, lt, NULL);
02190 #endif
02191 }
02192 #endif
02193 
02194 
02195 /* Retrieve an int from a config file */
02196                                                                                 
02197 static int retrieve_astcfgint(struct rpt *myrpt,char *category, char *name, int min, int max, int defl)
02198 {
02199         char *var;
02200         int ret;
02201    char include_zero = 0;
02202 
02203    if(min < 0){ /* If min is negative, this means include 0 as a valid entry */
02204       min = -min;
02205       include_zero = 1;
02206    }           
02207                                                                      
02208         var = (char *) ast_variable_retrieve(myrpt->cfg, category, name);
02209         if(var){
02210                 ret = myatoi(var);
02211       if(include_zero && !ret)
02212          return 0;
02213                 if(ret < min)
02214                         ret = min;
02215                 if(ret > max)
02216                         ret = max;
02217         }
02218         else
02219                 ret = defl;
02220         return ret;
02221 }
02222 
02223 
02224 static void load_rpt_vars(int n,int init)
02225 {
02226 char *this,*val;
02227 int   i,j,longestnode;
02228 struct ast_variable *vp;
02229 struct ast_config *cfg;
02230 char *strs[100];
02231 char s1[256];
02232 static char *cs_keywords[] = {"rptena","rptdis","apena","apdis","lnkena","lnkdis","totena","totdis","skena","skdis",
02233             "ufena","ufdis","atena","atdis",NULL};
02234 
02235    if (option_verbose > 2)
02236       ast_verbose(VERBOSE_PREFIX_3 "%s config for repeater %s\n",
02237          (init) ? "Loading initial" : "Re-Loading",rpt_vars[n].name);
02238    ast_mutex_lock(&rpt_vars[n].lock);
02239    if (rpt_vars[n].cfg) ast_config_destroy(rpt_vars[n].cfg);
02240 #ifdef   NEW_ASTERISK
02241    cfg = ast_config_load("rpt.conf",config_flags);
02242 #else
02243    cfg = ast_config_load("rpt.conf");
02244 #endif
02245    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
02246       ast_mutex_unlock(&rpt_vars[n].lock);
02247       ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf.  Radio Repeater disabled.\n");
02248       pthread_exit(NULL);
02249    }
02250    rpt_vars[n].cfg = cfg; 
02251    this = rpt_vars[n].name;
02252    memset(&rpt_vars[n].p,0,sizeof(rpt_vars[n].p));
02253    if (init)
02254    {
02255       char *cp;
02256       int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
02257 
02258       cp = (char *) &rpt_vars[n].p;
02259       memset(cp + sizeof(rpt_vars[n].p),0,
02260          sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
02261       rpt_vars[n].tele.next = &rpt_vars[n].tele;
02262       rpt_vars[n].tele.prev = &rpt_vars[n].tele;
02263       rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
02264       rpt_vars[n].tailmessagen = 0;
02265    }
02266 #ifdef   __RPT_NOTCH
02267    /* zot out filters stuff */
02268    memset(&rpt_vars[n].filters,0,sizeof(rpt_vars[n].filters));
02269 #endif
02270    val = (char *) ast_variable_retrieve(cfg,this,"context");
02271    if (val) rpt_vars[n].p.ourcontext = val;
02272    else rpt_vars[n].p.ourcontext = this;
02273    val = (char *) ast_variable_retrieve(cfg,this,"callerid");
02274    if (val) rpt_vars[n].p.ourcallerid = val;
02275    val = (char *) ast_variable_retrieve(cfg,this,"accountcode");
02276    if (val) rpt_vars[n].p.acctcode = val;
02277    val = (char *) ast_variable_retrieve(cfg,this,"idrecording");
02278    if (val) rpt_vars[n].p.ident = val;
02279    val = (char *) ast_variable_retrieve(cfg,this,"hangtime");
02280    if (val) rpt_vars[n].p.hangtime = atoi(val);
02281       else rpt_vars[n].p.hangtime = HANGTIME;
02282    val = (char *) ast_variable_retrieve(cfg,this,"althangtime");
02283    if (val) rpt_vars[n].p.althangtime = atoi(val);
02284       else rpt_vars[n].p.althangtime = HANGTIME;
02285    val = (char *) ast_variable_retrieve(cfg,this,"totime");
02286    if (val) rpt_vars[n].p.totime = atoi(val);
02287       else rpt_vars[n].p.totime = TOTIME;
02288    val = (char *) ast_variable_retrieve(cfg,this,"voxtimeout");
02289    if (val) rpt_vars[n].p.voxtimeout_ms = atoi(val);
02290       else rpt_vars[n].p.voxtimeout_ms = VOX_TIMEOUT_MS;
02291    val = (char *) ast_variable_retrieve(cfg,this,"voxrecover");
02292    if (val) rpt_vars[n].p.voxrecover_ms = atoi(val);
02293       else rpt_vars[n].p.voxrecover_ms = VOX_RECOVER_MS;
02294    val = (char *) ast_variable_retrieve(cfg,this,"simplexpatchdelay");
02295    if (val) rpt_vars[n].p.simplexpatchdelay = atoi(val);
02296       else rpt_vars[n].p.simplexpatchdelay = SIMPLEX_PATCH_DELAY;
02297    val = (char *) ast_variable_retrieve(cfg,this,"simplexphonedelay");
02298    if (val) rpt_vars[n].p.simplexphonedelay = atoi(val);
02299       else rpt_vars[n].p.simplexphonedelay = SIMPLEX_PHONE_DELAY;
02300    val = (char *) ast_variable_retrieve(cfg,this,"statpost_program");
02301    if (val) rpt_vars[n].p.statpost_program = val;
02302       else rpt_vars[n].p.statpost_program = STATPOST_PROGRAM;
02303    rpt_vars[n].p.statpost_url = 
02304       (char *) ast_variable_retrieve(cfg,this,"statpost_url");
02305    rpt_vars[n].p.tailmessagetime = retrieve_astcfgint(&rpt_vars[n],this, "tailmessagetime", 0, 2400000, 0);    
02306    rpt_vars[n].p.tailsquashedtime = retrieve_astcfgint(&rpt_vars[n],this, "tailsquashedtime", 0, 2400000, 0);     
02307    rpt_vars[n].p.duplex = retrieve_astcfgint(&rpt_vars[n],this,"duplex",0,4,2);
02308    rpt_vars[n].p.idtime = retrieve_astcfgint(&rpt_vars[n],this, "idtime", -60000, 2400000, IDTIME);   /* Enforce a min max including zero */
02309    rpt_vars[n].p.politeid = retrieve_astcfgint(&rpt_vars[n],this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */
02310    val = (char *) ast_variable_retrieve(cfg,this,"tonezone");
02311    if (val) rpt_vars[n].p.tonezone = val;
02312    rpt_vars[n].p.tailmessages[0] = 0;
02313    rpt_vars[n].p.tailmessagemax = 0;
02314    val = (char *) ast_variable_retrieve(cfg,this,"tailmessagelist");
02315    if (val) rpt_vars[n].p.tailmessagemax = finddelim(val, rpt_vars[n].p.tailmessages, 500);
02316    val = (char *) ast_variable_retrieve(cfg,this,"memory");
02317    if (!val) val = MEMORY;
02318    rpt_vars[n].p.memory = val;
02319    val = (char *) ast_variable_retrieve(cfg,this,"macro");
02320    if (!val) val = MACRO;
02321    rpt_vars[n].p.macro = val;
02322    val = (char *) ast_variable_retrieve(cfg,this,"tonemacro");
02323    if (!val) val = TONEMACRO;
02324    rpt_vars[n].p.tonemacro = val;
02325    val = (char *) ast_variable_retrieve(cfg,this,"startup_macro");
02326    if (val) rpt_vars[n].p.startupmacro = val;
02327    val = (char *) ast_variable_retrieve(cfg,this,"iobase");
02328    /* do not use atoi() here, we need to be able to have
02329       the input specified in hex or decimal so we use
02330       sscanf with a %i */
02331    if ((!val) || (sscanf(val,"%30i",&rpt_vars[n].p.iobase) != 1))
02332       rpt_vars[n].p.iobase = DEFAULT_IOBASE;
02333    val = (char *) ast_variable_retrieve(cfg,this,"ioport");
02334    rpt_vars[n].p.ioport = val;
02335    val = (char *) ast_variable_retrieve(cfg,this,"functions");
02336    if (!val)
02337       {
02338          val = FUNCTIONS;
02339          rpt_vars[n].p.simple = 1;
02340       } 
02341    rpt_vars[n].p.functions = val;
02342    val =  (char *) ast_variable_retrieve(cfg,this,"link_functions");
02343    if (val) rpt_vars[n].p.link_functions = val;
02344    else 
02345       rpt_vars[n].p.link_functions = rpt_vars[n].p.functions;
02346    val = (char *) ast_variable_retrieve(cfg,this,"phone_functions");
02347    if (val) rpt_vars[n].p.phone_functions = val;
02348    val = (char *) ast_variable_retrieve(cfg,this,"dphone_functions");
02349    if (val) rpt_vars[n].p.dphone_functions = val;
02350    val = (char *) ast_variable_retrieve(cfg,this,"alt_functions");
02351    if (val) rpt_vars[n].p.alt_functions = val;
02352    val = (char *) ast_variable_retrieve(cfg,this,"funcchar");
02353    if (!val) rpt_vars[n].p.funcchar = FUNCCHAR; else 
02354       rpt_vars[n].p.funcchar = *val;      
02355    val = (char *) ast_variable_retrieve(cfg,this,"endchar");
02356    if (!val) rpt_vars[n].p.endchar = ENDCHAR; else 
02357       rpt_vars[n].p.endchar = *val;    
02358    val = (char *) ast_variable_retrieve(cfg,this,"nobusyout");
02359    if (val) rpt_vars[n].p.nobusyout = ast_true(val);
02360    val = (char *) ast_variable_retrieve(cfg,this,"notelemtx");
02361    if (val) rpt_vars[n].p.notelemtx = ast_true(val);
02362    val = (char *) ast_variable_retrieve(cfg,this,"propagate_dtmf");
02363    if (val) rpt_vars[n].p.propagate_dtmf = ast_true(val);
02364    val = (char *) ast_variable_retrieve(cfg,this,"propagate_phonedtmf");
02365    if (val) rpt_vars[n].p.propagate_phonedtmf = ast_true(val);
02366    val = (char *) ast_variable_retrieve(cfg,this,"linktolink");
02367    if (val) rpt_vars[n].p.linktolink = ast_true(val);
02368    val = (char *) ast_variable_retrieve(cfg,this,"nodes");
02369    if (!val) val = NODES;
02370    rpt_vars[n].p.nodes = val;
02371    val = (char *) ast_variable_retrieve(cfg,this,"extnodes");
02372    if (!val) val = EXTNODES;
02373    rpt_vars[n].p.extnodes = val;
02374    val = (char *) ast_variable_retrieve(cfg,this,"extnodefile");
02375    if (!val) val = EXTNODEFILE;
02376    rpt_vars[n].p.extnodefile = val;
02377    val = (char *) ast_variable_retrieve(cfg,this,"archivedir");
02378    if (val) rpt_vars[n].p.archivedir = val;
02379    val = (char *) ast_variable_retrieve(cfg,this,"authlevel");
02380    if (val) rpt_vars[n].p.authlevel = atoi(val); 
02381    else rpt_vars[n].p.authlevel = 0;
02382    val = (char *) ast_variable_retrieve(cfg,this,"parrot");
02383    if (val) rpt_vars[n].p.parrotmode = ast_true(val) * 2;
02384    else rpt_vars[n].p.parrotmode = 0;
02385    val = (char *) ast_variable_retrieve(cfg,this,"parrottime");
02386    if (val) rpt_vars[n].p.parrottime = atoi(val); 
02387    else rpt_vars[n].p.parrottime = PARROTTIME;
02388    val = (char *) ast_variable_retrieve(cfg,this,"rptnode");
02389    rpt_vars[n].p.rptnode = val;
02390    val = (char *) ast_variable_retrieve(cfg,this,"mars");
02391    if (val) rpt_vars[n].p.remote_mars = atoi(val); 
02392    else rpt_vars[n].p.remote_mars = 0;
02393    val = (char *) ast_variable_retrieve(cfg,this,"monminblocks");
02394    if (val) rpt_vars[n].p.monminblocks = atol(val); 
02395    else rpt_vars[n].p.monminblocks = DEFAULT_MONITOR_MIN_DISK_BLOCKS;
02396    val = (char *) ast_variable_retrieve(cfg,this,"remote_inact_timeout");
02397    if (val) rpt_vars[n].p.remoteinacttimeout = atoi(val); 
02398    else rpt_vars[n].p.remoteinacttimeout = DEFAULT_REMOTE_INACT_TIMEOUT;
02399    val = (char *) ast_variable_retrieve(cfg,this,"civaddr");
02400    if (val) rpt_vars[n].p.civaddr = atoi(val); 
02401    else rpt_vars[n].p.civaddr = DEFAULT_CIV_ADDR;
02402    val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout");
02403    if (val) rpt_vars[n].p.remotetimeout = atoi(val); 
02404    else rpt_vars[n].p.remotetimeout = DEFAULT_REMOTE_TIMEOUT;
02405    val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout_warning");
02406    if (val) rpt_vars[n].p.remotetimeoutwarning = atoi(val); 
02407    else rpt_vars[n].p.remotetimeoutwarning = DEFAULT_REMOTE_TIMEOUT_WARNING;
02408    val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout_warning_freq");
02409    if (val) rpt_vars[n].p.remotetimeoutwarningfreq = atoi(val); 
02410    else rpt_vars[n].p.remotetimeoutwarningfreq = DEFAULT_REMOTE_TIMEOUT_WARNING_FREQ;
02411 #ifdef   __RPT_NOTCH
02412    val = (char *) ast_variable_retrieve(cfg,this,"rxnotch");
02413    if (val) {
02414       i = finddelim(val,strs,MAXFILTERS * 2);
02415       i &= ~1; /* force an even number, rounded down */
02416       if (i >= 2) for(j = 0; j < i; j += 2)
02417       {
02418          rpt_mknotch(atof(strs[j]),atof(strs[j + 1]),
02419            &rpt_vars[n].filters[j >> 1].gain,
02420              &rpt_vars[n].filters[j >> 1].const0,
02421             &rpt_vars[n].filters[j >> 1].const1,
02422                 &rpt_vars[n].filters[j >> 1].const2);
02423          sprintf(rpt_vars[n].filters[j >> 1].desc,"%s Hz, BW = %s",
02424             strs[j],strs[j + 1]);
02425       }
02426 
02427    }
02428 #endif
02429    val = (char *) ast_variable_retrieve(cfg,this,"inxlat");
02430    if (val) {
02431       memset(&rpt_vars[n].p.inxlat,0,sizeof(struct rpt_xlat));
02432       i = finddelim(val,strs,3);
02433       if (i) strncpy(rpt_vars[n].p.inxlat.funccharseq,strs[0],MAXXLAT - 1);
02434       if (i > 1) strncpy(rpt_vars[n].p.inxlat.endcharseq,strs[1],MAXXLAT - 1);
02435       if (i > 2) strncpy(rpt_vars[n].p.inxlat.passchars,strs[2],MAXXLAT - 1);
02436    }
02437    val = (char *) ast_variable_retrieve(cfg,this,"outxlat");
02438    if (val) {
02439       memset(&rpt_vars[n].p.outxlat,0,sizeof(struct rpt_xlat));
02440       i = finddelim(val,strs,3);
02441       if (i) strncpy(rpt_vars[n].p.outxlat.funccharseq,strs[0],MAXXLAT - 1);
02442       if (i > 1) strncpy(rpt_vars[n].p.outxlat.endcharseq,strs[1],MAXXLAT - 1);
02443       if (i > 2) strncpy(rpt_vars[n].p.outxlat.passchars,strs[2],MAXXLAT - 1);
02444    }
02445    /* retreive the stanza name for the control states if there is one */
02446    val = (char *) ast_variable_retrieve(cfg,this,"controlstates");
02447    rpt_vars[n].p.csstanzaname = val;
02448       
02449    /* retreive the stanza name for the scheduler if there is one */
02450    val = (char *) ast_variable_retrieve(cfg,this,"scheduler");
02451    rpt_vars[n].p.skedstanzaname = val;
02452 
02453    /* retreive the stanza name for the txlimits */
02454    val = (char *) ast_variable_retrieve(cfg,this,"txlimits");
02455    rpt_vars[n].p.txlimitsstanzaname = val;
02456 
02457    longestnode = 0;
02458 
02459    vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes);
02460       
02461    while(vp){
02462       j = strlen(vp->name);
02463       if (j > longestnode)
02464          longestnode = j;
02465       vp = vp->next;
02466    }
02467 
02468    rpt_vars[n].longestnode = longestnode;
02469       
02470    /*
02471    * For this repeater, Determine the length of the longest function 
02472    */
02473    rpt_vars[n].longestfunc = 0;
02474    vp = ast_variable_browse(cfg, rpt_vars[n].p.functions);
02475    while(vp){
02476       j = strlen(vp->name);
02477       if (j > rpt_vars[n].longestfunc)
02478          rpt_vars[n].longestfunc = j;
02479       vp = vp->next;
02480    }
02481    /*
02482    * For this repeater, Determine the length of the longest function 
02483    */
02484    rpt_vars[n].link_longestfunc = 0;
02485    vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions);
02486    while(vp){
02487       j = strlen(vp->name);
02488       if (j > rpt_vars[n].link_longestfunc)
02489          rpt_vars[n].link_longestfunc = j;
02490       vp = vp->next;
02491    }
02492    rpt_vars[n].phone_longestfunc = 0;
02493    if (rpt_vars[n].p.phone_functions)
02494    {
02495       vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions);
02496       while(vp){
02497          j = strlen(vp->name);
02498          if (j > rpt_vars[n].phone_longestfunc)
02499             rpt_vars[n].phone_longestfunc = j;
02500          vp = vp->next;
02501       }
02502    }
02503    rpt_vars[n].dphone_longestfunc = 0;
02504    if (rpt_vars[n].p.dphone_functions)
02505    {
02506       vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions);
02507       while(vp){
02508          j = strlen(vp->name);
02509          if (j > rpt_vars[n].dphone_longestfunc)
02510             rpt_vars[n].dphone_longestfunc = j;
02511          vp = vp->next;
02512       }
02513    }
02514    rpt_vars[n].alt_longestfunc = 0;
02515    if (rpt_vars[n].p.alt_functions)
02516    {
02517       vp = ast_variable_browse(cfg, rpt_vars[n].p.alt_functions);
02518       while(vp){
02519          j = strlen(vp->name);
02520          if (j > rpt_vars[n].alt_longestfunc)
02521             rpt_vars[n].alt_longestfunc = j;
02522          vp = vp->next;
02523       }
02524    }
02525    rpt_vars[n].macro_longest = 1;
02526    vp = ast_variable_browse(cfg, rpt_vars[n].p.macro);
02527    while(vp){
02528       j = strlen(vp->name);
02529       if (j > rpt_vars[n].macro_longest)
02530          rpt_vars[n].macro_longest = j;
02531       vp = vp->next;
02532    }
02533    
02534    /* Browse for control states */
02535    if(rpt_vars[n].p.csstanzaname)
02536       vp = ast_variable_browse(cfg, rpt_vars[n].p.csstanzaname);
02537    else
02538       vp = NULL;
02539    for( i = 0 ; vp && (i < MAX_SYSSTATES) ; i++){ /* Iterate over the number of control state lines in the stanza */
02540       int k,nukw,statenum;
02541       statenum=atoi(vp->name);
02542       strncpy(s1, vp->value, 255);
02543       s1[255] = 0;
02544       nukw  = finddelim(s1,strs,32);
02545       
02546       for (k = 0 ; k < nukw ; k++){ /* for each user specified keyword */  
02547          for(j = 0 ; cs_keywords[j] != NULL ; j++){ /* try to match to one in our internal table */
02548             if(!strcmp(strs[k],cs_keywords[j])){
02549                switch(j){
02550                   case 0: /* rptena */
02551                      rpt_vars[n].p.s[statenum].txdisable = 0;
02552                      break;
02553                   case 1: /* rptdis */
02554                      rpt_vars[n].p.s[statenum].txdisable = 1;
02555                      break;
02556          
02557                   case 2: /* apena */
02558                      rpt_vars[n].p.s[statenum].autopatchdisable = 0;
02559                      break;
02560 
02561                   case 3: /* apdis */
02562                      rpt_vars[n].p.s[statenum].autopatchdisable = 1;
02563                      break;
02564 
02565                   case 4: /* lnkena */
02566                      rpt_vars[n].p.s[statenum].linkfundisable = 0;
02567                      break;
02568    
02569                   case 5: /* lnkdis */
02570                      rpt_vars[n].p.s[statenum].linkfundisable = 1;
02571                      break;
02572 
02573                   case 6: /* totena */
02574                      rpt_vars[n].p.s[statenum].totdisable = 0;
02575                      break;
02576                
02577                   case 7: /* totdis */
02578                      rpt_vars[n].p.s[statenum].totdisable = 1;
02579                      break;
02580 
02581                   case 8: /* skena */
02582                      rpt_vars[n].p.s[statenum].schedulerdisable = 0;
02583                      break;
02584 
02585                   case 9: /* skdis */
02586                      rpt_vars[n].p.s[statenum].schedulerdisable = 1;
02587                      break;
02588 
02589                   case 10: /* ufena */
02590                      rpt_vars[n].p.s[statenum].userfundisable = 0;
02591                      break;
02592 
02593                   case 11: /* ufdis */
02594                      rpt_vars[n].p.s[statenum].userfundisable = 1;
02595                      break;
02596 
02597                   case 12: /* atena */
02598                      rpt_vars[n].p.s[statenum].alternatetail = 1;
02599                      break;
02600 
02601                   case 13: /* atdis */
02602                      rpt_vars[n].p.s[statenum].alternatetail = 0;
02603                      break;
02604          
02605                   default:
02606                      ast_log(LOG_WARNING,
02607                         "Unhandled control state keyword %s", cs_keywords[i]);
02608                      break;
02609                }
02610             }
02611          }
02612       }
02613       vp = vp->next;
02614    }
02615    ast_mutex_unlock(&rpt_vars[n].lock);
02616 }
02617 
02618 /*
02619 * Enable or disable debug output at a given level at the console
02620 */
02621                                                                                                                                  
02622 static int rpt_do_debug(int fd, int argc, char *argv[])
02623 {
02624    int newlevel;
02625 
02626         if (argc != 4)
02627                 return RESULT_SHOWUSAGE;
02628         newlevel = myatoi(argv[3]);
02629         if((newlevel < 0) || (newlevel > 7))
02630                 return RESULT_SHOWUSAGE;
02631         if(newlevel)
02632                 ast_cli(fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
02633         else
02634                 ast_cli(fd, "app_rpt Debugging disabled\n");
02635 
02636         debug = newlevel;                                                                                                                          
02637         return RESULT_SUCCESS;
02638 }
02639 
02640 /*
02641 * Dump rpt struct debugging onto console
02642 */
02643                                                                                                                                  
02644 static int rpt_do_dump(int fd, int argc, char *argv[])
02645 {
02646    int i;
02647 
02648         if (argc != 3)
02649                 return RESULT_SHOWUSAGE;
02650 
02651    for(i = 0; i < nrpts; i++)
02652    {
02653       if (!strcmp(argv[2],rpt_vars[i].name))
02654       {
02655          rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
02656               ast_cli(fd, "app_rpt struct dump requested for node %s\n",argv[2]);
02657               return RESULT_SUCCESS;
02658       }
02659    }
02660    return RESULT_FAILURE;
02661 }
02662 
02663 /*
02664 * Dump statistics onto console
02665 */
02666 
02667 static int rpt_do_stats(int fd, int argc, char *argv[])
02668 {
02669    int i,j,numoflinks;
02670    int dailytxtime, dailykerchunks;
02671    time_t now;
02672    int totalkerchunks, dailykeyups, totalkeyups, timeouts;
02673    int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
02674    int uptime;
02675    long long totaltxtime;
02676    struct   rpt_link *l;
02677    char *listoflinks[MAX_STAT_LINKS];  
02678    char *lastdtmfcommand,*parrot_ena;
02679    char *tot_state, *ider_state, *patch_state;
02680    char *reverse_patch_state, *sys_ena, *tot_ena, *link_ena, *patch_ena;
02681    char *sch_ena, *input_signal, *called_number, *user_funs, *tail_type;
02682    struct rpt *myrpt;
02683 
02684    static char *not_applicable = "N/A";
02685 
02686    if(argc != 3)
02687       return RESULT_SHOWUSAGE;
02688 
02689    tot_state = ider_state = 
02690    patch_state = reverse_patch_state = 
02691    input_signal = not_applicable;
02692    called_number = lastdtmfcommand = NULL;
02693 
02694    time(&now);
02695    for(i = 0; i < nrpts; i++)
02696    {
02697       if (!strcmp(argv[2],rpt_vars[i].name)){
02698          /* Make a copy of all stat variables while locked */
02699          myrpt = &rpt_vars[i];
02700          rpt_mutex_lock(&myrpt->lock); /* LOCK */
02701          uptime = (int)(now - starttime);
02702          dailytxtime = myrpt->dailytxtime;
02703          totaltxtime = myrpt->totaltxtime;
02704          dailykeyups = myrpt->dailykeyups;
02705          totalkeyups = myrpt->totalkeyups;
02706          dailykerchunks = myrpt->dailykerchunks;
02707          totalkerchunks = myrpt->totalkerchunks;
02708          dailyexecdcommands = myrpt->dailyexecdcommands;
02709          totalexecdcommands = myrpt->totalexecdcommands;
02710          timeouts = myrpt->timeouts;
02711 
02712          /* Traverse the list of connected nodes */
02713          reverse_patch_state = "DOWN";
02714          numoflinks = 0;
02715          l = myrpt->links.next;
02716          while(l && (l != &myrpt->links)){
02717             if(numoflinks >= MAX_STAT_LINKS){
02718                ast_log(LOG_NOTICE,
02719                "maximum number of links exceeds %d in rpt_do_stats()!",MAX_STAT_LINKS);
02720                break;
02721             }
02722             if (l->name[0] == '0'){ /* Skip '0' nodes */
02723                reverse_patch_state = "UP";
02724                l = l->next;
02725                continue;
02726             }
02727             listoflinks[numoflinks] = ast_strdup(l->name);
02728             if(listoflinks[numoflinks] == NULL){
02729                break;
02730             }
02731             else{
02732                numoflinks++;
02733             }
02734             l = l->next;
02735          }
02736 
02737          if(myrpt->keyed)
02738             input_signal = "YES";
02739          else
02740             input_signal = "NO";
02741 
02742          if(myrpt->p.parrotmode)
02743             parrot_ena = "ENABLED";
02744          else
02745             parrot_ena = "DISABLED";
02746 
02747          if(myrpt->p.s[myrpt->p.sysstate_cur].txdisable)
02748             sys_ena = "DISABLED";
02749          else
02750             sys_ena = "ENABLED";
02751 
02752          if(myrpt->p.s[myrpt->p.sysstate_cur].totdisable)
02753             tot_ena = "DISABLED";
02754          else
02755             tot_ena = "ENABLED";
02756 
02757          if(myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable)
02758             link_ena = "DISABLED";
02759          else
02760             link_ena = "ENABLED";
02761 
02762          if(myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable)
02763             patch_ena = "DISABLED";
02764          else
02765             patch_ena = "ENABLED";
02766 
02767          if(myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable)
02768             sch_ena = "DISABLED";
02769          else
02770             sch_ena = "ENABLED";
02771 
02772          if(myrpt->p.s[myrpt->p.sysstate_cur].userfundisable)
02773             user_funs = "DISABLED";
02774          else
02775             user_funs = "ENABLED";
02776 
02777          if(myrpt->p.s[myrpt->p.sysstate_cur].alternatetail)
02778             tail_type = "ALTERNATE";
02779          else
02780             tail_type = "STANDARD";
02781 
02782          if(!myrpt->totimer)
02783             tot_state = "TIMED OUT!";
02784          else if(myrpt->totimer != myrpt->p.totime)
02785             tot_state = "ARMED";
02786          else
02787             tot_state = "RESET";
02788 
02789          if(myrpt->tailid)
02790             ider_state = "QUEUED IN TAIL";
02791          else if(myrpt->mustid)
02792             ider_state = "QUEUED FOR CLEANUP";
02793          else
02794             ider_state = "CLEAN";
02795 
02796          switch(myrpt->callmode){
02797             case 1:
02798                patch_state = "DIALING";
02799                break;
02800             case 2:
02801                patch_state = "CONNECTING";
02802                break;
02803             case 3:
02804                patch_state = "UP";
02805                break;
02806 
02807             case 4:
02808                patch_state = "CALL FAILED";
02809                break;
02810 
02811             default:
02812                patch_state = "DOWN";
02813          }
02814 
02815          if(strlen(myrpt->exten)){
02816             called_number = ast_strdup(myrpt->exten);
02817          }
02818 
02819          if(strlen(myrpt->lastdtmfcommand)){
02820             lastdtmfcommand = ast_strdup(myrpt->lastdtmfcommand);
02821          }
02822          rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
02823 
02824          ast_cli(fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
02825          ast_cli(fd, "Selected system state............................: %d\n", myrpt->p.sysstate_cur);
02826          ast_cli(fd, "Signal on input..................................: %s\n", input_signal);
02827          ast_cli(fd, "System...........................................: %s\n", sys_ena);
02828          ast_cli(fd, "Parrot Mode......................................: %s\n", parrot_ena);
02829          ast_cli(fd, "Scheduler........................................: %s\n", sch_ena);
02830          ast_cli(fd, "Tail Time........................................: %s\n", tail_type);
02831          ast_cli(fd, "Time out timer...................................: %s\n", tot_ena);
02832          ast_cli(fd, "Time out timer state.............................: %s\n", tot_state);
02833          ast_cli(fd, "Time outs since system initialization............: %d\n", timeouts);
02834          ast_cli(fd, "Identifier state.................................: %s\n", ider_state);
02835          ast_cli(fd, "Kerchunks today..................................: %d\n", dailykerchunks);
02836          ast_cli(fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
02837          ast_cli(fd, "Keyups today.....................................: %d\n", dailykeyups);
02838          ast_cli(fd, "Keyups since system initialization...............: %d\n", totalkeyups);
02839          ast_cli(fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
02840          ast_cli(fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
02841          ast_cli(fd, "Last DTMF command executed.......................: %s\n", 
02842          (lastdtmfcommand && strlen(lastdtmfcommand)) ? lastdtmfcommand : not_applicable);
02843          hours = dailytxtime/3600000;
02844          dailytxtime %= 3600000;
02845          minutes = dailytxtime/60000;
02846          dailytxtime %= 60000;
02847          seconds = dailytxtime/1000;
02848          dailytxtime %= 1000;
02849 
02850          ast_cli(fd, "TX time today....................................: %02d:%02d:%02d.%d\n",
02851             hours, minutes, seconds, dailytxtime);
02852 
02853          hours = (int) totaltxtime/3600000;
02854          totaltxtime %= 3600000;
02855          minutes = (int) totaltxtime/60000;
02856          totaltxtime %= 60000;
02857          seconds = (int)  totaltxtime/1000;
02858          totaltxtime %= 1000;
02859 
02860          ast_cli(fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
02861              hours, minutes, seconds, (int) totaltxtime);
02862 
02863                         hours = uptime/3600;
02864                         uptime %= 3600;
02865                         minutes = uptime/60;
02866                         uptime %= 60;
02867 
02868                         ast_cli(fd, "Uptime...........................................: %02d:%02d:%02d\n",
02869                                 hours, minutes, uptime);
02870 
02871          ast_cli(fd, "Nodes currently connected to us..................: ");
02872                         if(!numoflinks){
02873                          ast_cli(fd,"<NONE>");
02874                         }
02875          else{
02876             for(j = 0 ;j < numoflinks; j++){
02877                ast_cli(fd, "%s", listoflinks[j]);
02878                if(j % 4 == 3){
02879                   ast_cli(fd, "\n");
02880                   ast_cli(fd, "                                                 : ");
02881                }  
02882                else{
02883                   if((numoflinks - 1) - j  > 0)
02884                      ast_cli(fd, ", ");
02885                }
02886             }
02887          }
02888          ast_cli(fd,"\n");
02889 
02890          ast_cli(fd, "Autopatch........................................: %s\n", patch_ena);
02891          ast_cli(fd, "Autopatch state..................................: %s\n", patch_state);
02892          ast_cli(fd, "Autopatch called number..........................: %s\n",
02893          (called_number && strlen(called_number)) ? called_number : not_applicable);
02894          ast_cli(fd, "Reverse patch/IAXRPT connected...................: %s\n", reverse_patch_state);
02895          ast_cli(fd, "User linking commands............................: %s\n", link_ena);
02896          ast_cli(fd, "User functions...................................: %s\n\n", user_funs);
02897 
02898          for(j = 0; j < numoflinks; j++){ /* ast_free() all link names */
02899             ast_free(listoflinks[j]);
02900          }
02901          if(called_number){
02902             ast_free(called_number);
02903          }
02904          if(lastdtmfcommand){
02905             ast_free(lastdtmfcommand);
02906          }
02907               return RESULT_SUCCESS;
02908       }
02909    }
02910    return RESULT_FAILURE;
02911 }
02912 
02913 /*
02914 * Link stats function
02915 */
02916 
02917 static int rpt_do_lstats(int fd, int argc, char *argv[])
02918 {
02919    int i,j;
02920    char *connstate;
02921    struct rpt *myrpt;
02922    struct rpt_link *l;
02923    struct rpt_lstat *s,*t;
02924    struct rpt_lstat s_head;
02925    if(argc != 3)
02926       return RESULT_SHOWUSAGE;
02927 
02928    s = NULL;
02929    s_head.next = &s_head;
02930    s_head.prev = &s_head;
02931 
02932    for(i = 0; i < nrpts; i++)
02933    {
02934       if (!strcmp(argv[2],rpt_vars[i].name)){
02935          /* Make a copy of all stat variables while locked */
02936          myrpt = &rpt_vars[i];
02937          rpt_mutex_lock(&myrpt->lock); /* LOCK */
02938          /* Traverse the list of connected nodes */
02939          j = 0;
02940          l = myrpt->links.next;
02941          while(l && (l != &myrpt->links)){
02942             if (l->name[0] == '0'){ /* Skip '0' nodes */
02943                l = l->next;
02944                continue;
02945             }
02946             if((s = (struct rpt_lstat *) ast_malloc(sizeof(struct rpt_lstat))) == NULL){
02947                ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
02948                rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
02949                return RESULT_FAILURE;
02950             }
02951             memset(s, 0, sizeof(struct rpt_lstat));
02952             strncpy(s->name, l->name, MAXREMSTR - 1);
02953             if (l->chan) pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
02954             else strcpy(s->peer,"(none)");
02955             s->mode = l->mode;
02956             s->outbound = l->outbound;
02957             s->reconnects = l->reconnects;
02958             s->connecttime = l->connecttime;
02959             s->thisconnected = l->thisconnected;
02960             memcpy(s->chan_stat,l->chan_stat,NRPTSTAT * sizeof(struct rpt_chan_stat));
02961             insque((struct qelem *) s, (struct qelem *) s_head.next);
02962             memset(l->chan_stat,0,NRPTSTAT * sizeof(struct rpt_chan_stat));
02963             l = l->next;
02964          }
02965          rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
02966          ast_cli(fd, "NODE      PEER                RECONNECTS  DIRECTION  CONNECT TIME        CONNECT STATE\n");
02967          ast_cli(fd, "----      ----                ----------  ---------  ------------        -------------\n");
02968 
02969          for(s = s_head.next; s != &s_head; s = s->next){
02970             int hours, minutes, seconds;
02971             long long connecttime = s->connecttime;
02972             char conntime[21];
02973             hours = (int) connecttime/3600000;
02974             connecttime %= 3600000;
02975             minutes = (int) connecttime/60000;
02976             connecttime %= 60000;
02977             seconds = (int)  connecttime/1000;
02978             connecttime %= 1000;
02979             snprintf(conntime, 20, "%02d:%02d:%02d.%d",
02980                hours, minutes, seconds, (int) connecttime);
02981             conntime[20] = 0;
02982             if(s->thisconnected)
02983                connstate  = "ESTABLISHED";
02984             else
02985                connstate = "CONNECTING";
02986             ast_cli(fd, "%-10s%-20s%-12d%-11s%-20s%-20s\n",
02987                s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime, connstate);
02988          }  
02989          /* destroy our local link queue */
02990          s = s_head.next;
02991          while(s != &s_head){
02992             t = s;
02993             s = s->next;
02994             remque((struct qelem *)t);
02995             ast_free(t);
02996          }        
02997          return RESULT_SUCCESS;
02998       }
02999    }
03000    return RESULT_FAILURE;
03001 }
03002 
03003 /*
03004 * List all nodes connected, directly or indirectly
03005 */
03006 
03007 static int rpt_do_nodes(int fd, int argc, char *argv[])
03008 {
03009    int i,j;
03010    char ns;
03011    char lbuf[MAXLINKLIST],*strs[MAXLINKLIST];
03012    struct rpt *myrpt;
03013    if(argc != 3)
03014       return RESULT_SHOWUSAGE;
03015 
03016    for(i = 0; i < nrpts; i++)
03017    {
03018       if (!strcmp(argv[2],rpt_vars[i].name)){
03019          /* Make a copy of all stat variables while locked */
03020          myrpt = &rpt_vars[i];
03021          rpt_mutex_lock(&myrpt->lock); /* LOCK */
03022          __mklinklist(myrpt,NULL,lbuf);
03023          rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
03024          /* parse em */
03025          ns = finddelim(lbuf,strs,MAXLINKLIST);
03026          /* sort em */
03027          if (ns) qsort((void *)strs,ns,sizeof(char *),mycompar);
03028          ast_cli(fd,"\n");
03029          ast_cli(fd, "************************* CONNECTED NODES *************************\n\n");
03030          for(j = 0 ;; j++){
03031             if(!strs[j]){
03032                if(!j){
03033                   ast_cli(fd,"<NONE>");
03034                }
03035                break;
03036             }
03037             ast_cli(fd, "%s", strs[j]);
03038             if(j % 8 == 7){
03039                ast_cli(fd, "\n");
03040             }
03041             else{
03042                if(strs[j + 1])
03043                   ast_cli(fd, ", ");
03044             }
03045          }
03046          ast_cli(fd,"\n\n");
03047          return RESULT_SUCCESS;
03048       }
03049    }
03050    return RESULT_FAILURE;
03051 }
03052 
03053 /*
03054 * List all locally configured nodes
03055 */
03056 
03057 static int rpt_do_local_nodes(int fd, int argc, char *argv[])
03058 {
03059 
03060     int i;
03061     ast_cli(fd, "\nNode\n----\n");
03062     for (i=0; i< nrpts; i++)
03063     {
03064         ast_cli(fd, "%s\n", rpt_vars[i].name);        
03065     } /* for i */
03066     ast_cli(fd,"\n");
03067     return RESULT_SUCCESS;
03068 } 
03069 
03070 
03071 /*
03072 * reload vars 
03073 */
03074 
03075 static int rpt_do_reload(int fd, int argc, char *argv[])
03076 {
03077 int   n;
03078 
03079         if (argc > 2) return RESULT_SHOWUSAGE;
03080 
03081    for(n = 0; n < nrpts; n++) rpt_vars[n].reload = 1;
03082 
03083    return RESULT_FAILURE;
03084 }
03085 
03086 /*
03087 * restart app_rpt
03088 */
03089                                                                                                                                  
03090 static int rpt_do_restart(int fd, int argc, char *argv[])
03091 {
03092 int   i;
03093 
03094         if (argc > 2) return RESULT_SHOWUSAGE;
03095    for(i = 0; i < nrpts; i++)
03096    {
03097       if (rpt_vars[i].rxchannel) ast_softhangup(rpt_vars[i].rxchannel,AST_SOFTHANGUP_DEV);
03098    }
03099    return RESULT_FAILURE;
03100 }
03101 
03102 
03103 /*
03104 * send an app_rpt DTMF function from the CLI
03105 */
03106                                                                                                                                  
03107 static int rpt_do_fun(int fd, int argc, char *argv[])
03108 {
03109    int   i,busy=0;
03110 
03111         if (argc != 4) return RESULT_SHOWUSAGE;
03112 
03113    for(i = 0; i < nrpts; i++){
03114       if(!strcmp(argv[2], rpt_vars[i].name)){
03115          struct rpt *myrpt = &rpt_vars[i];
03116          rpt_mutex_lock(&myrpt->lock);
03117          if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(argv[3])){
03118             rpt_mutex_unlock(&myrpt->lock);
03119             busy=1;
03120          }
03121          if(!busy){
03122             myrpt->macrotimer = MACROTIME;
03123             strncat(myrpt->macrobuf,argv[3],MAXMACRO - 1);
03124          }
03125          rpt_mutex_unlock(&myrpt->lock);
03126       }
03127    }
03128    if(busy){
03129       ast_cli(fd, "Function decoder busy");
03130    }
03131    return RESULT_FAILURE;
03132 }
03133 /*
03134    the convention is that macros in the data from the rpt() application
03135    are all at the end of the data, separated by the | and start with a *
03136    when put into the macro buffer, the characters have their high bit
03137    set so the macro processor knows they came from the application data
03138    and to use the alt-functions table.
03139    sph:
03140 */
03141 static int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
03142 {
03143    int   busy=0;
03144 
03145    rpt_mutex_lock(&myrpt->lock);
03146    if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)){
03147       rpt_mutex_unlock(&myrpt->lock);
03148       busy=1;
03149    }
03150    if(!busy){
03151       int x;
03152       if (debug)ast_log(LOG_NOTICE, "rpt_push_alt_macro %s\n",sptr);
03153       myrpt->macrotimer = MACROTIME;
03154       for(x = 0; *(sptr + x); x++)
03155           myrpt->macrobuf[x] = *(sptr + x) | 0x80;
03156       *(sptr + x) = 0;
03157    }
03158    rpt_mutex_unlock(&myrpt->lock);
03159 
03160    if(busy)ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
03161 
03162    return busy;
03163 }
03164 /*
03165    allows us to test rpt() application data commands
03166 */
03167 static int rpt_do_fun1(int fd, int argc, char *argv[])
03168 {
03169    int   i;
03170 
03171     if (argc != 4) return RESULT_SHOWUSAGE;
03172 
03173    for(i = 0; i < nrpts; i++){
03174       if(!strcmp(argv[2], rpt_vars[i].name)){
03175          struct rpt *myrpt = &rpt_vars[i];
03176          rpt_push_alt_macro(myrpt,argv[3]);
03177       }
03178    }
03179    return RESULT_FAILURE;
03180 }
03181 /*
03182 * send an app_rpt **command** from the CLI
03183 */
03184 
03185 static int rpt_do_cmd(int fd, int argc, char *argv[])
03186 {
03187    int i, l;
03188    int busy=0;
03189    int maxActions = sizeof(function_table)/sizeof(struct function_table_tag);
03190 
03191    int thisRpt = -1;
03192    int thisAction = -1;
03193    struct rpt *myrpt = NULL;
03194    if (argc != 6) return RESULT_SHOWUSAGE;
03195    
03196    for(i = 0; i < nrpts; i++)
03197    {
03198       if(!strcmp(argv[2], rpt_vars[i].name))
03199       {
03200          thisRpt = i;
03201          myrpt = &rpt_vars[i];
03202          break;
03203       } /* if !strcmp... */
03204    } /* for i */
03205 
03206    if (thisRpt < 0)
03207    {
03208       ast_cli(fd, "Unknown node number %s.\n", argv[2]);
03209       return RESULT_FAILURE;
03210    } /* if thisRpt < 0 */
03211    
03212    /* Look up the action */
03213    l = strlen(argv[3]);
03214    for(i = 0 ; i < maxActions; i++)
03215    {
03216       if(!strncasecmp(argv[3], function_table[i].action, l))
03217       {
03218          thisAction = i;
03219          break;
03220       } /* if !strncasecmp... */
03221    } /* for i */
03222    
03223    if (thisAction < 0)
03224    {
03225       ast_cli(fd, "Unknown action name %s.\n", argv[3]);
03226       return RESULT_FAILURE;
03227    } /* if thisAction < 0 */
03228 
03229    /* at this point, it looks like all the arguments make sense... */
03230 
03231    rpt_mutex_lock(&myrpt->lock);
03232 
03233    if (rpt_vars[thisRpt].cmdAction.state == CMD_STATE_IDLE)
03234    {
03235       rpt_vars[thisRpt].cmdAction.state = CMD_STATE_BUSY;
03236       rpt_vars[thisRpt].cmdAction.functionNumber = thisAction;
03237       strncpy(rpt_vars[thisRpt].cmdAction.param, argv[4], MAXDTMF);
03238       strncpy(rpt_vars[thisRpt].cmdAction.digits, argv[5], MAXDTMF);
03239       rpt_vars[thisRpt].cmdAction.command_source = SOURCE_RPT;
03240       rpt_vars[thisRpt].cmdAction.state = CMD_STATE_READY;
03241    } /* if (rpt_vars[thisRpt].cmdAction.state == CMD_STATE_IDLE */
03242    else
03243    {
03244       busy = 1;
03245    } /* if (rpt_vars[thisRpt].cmdAction.state == CMD_STATE_IDLE */
03246    rpt_mutex_unlock(&myrpt->lock);
03247 
03248    return (busy ? RESULT_FAILURE : RESULT_SUCCESS);
03249 } /* rpt_do_cmd() */
03250 
03251 static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
03252 {
03253    int res;
03254 
03255         if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
03256                 return res;
03257                                                                                                                                             
03258         while(chan->generatordata) {
03259       if (ast_safe_sleep(chan,1)) return -1;
03260    }
03261 
03262         return 0;
03263 }
03264 
03265 static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
03266 {
03267    return play_tone_pair(chan, freq, 0, duration, amplitude);
03268 }
03269 
03270 static int play_silence(struct ast_channel *chan, int duration)
03271 {
03272    return play_tone_pair(chan, 0, 0, duration, 0);
03273 }
03274 
03275 #ifdef   NEW_ASTERISK
03276 
03277 static char *res2cli(int r)
03278 
03279 {
03280    switch (r)
03281    {
03282        case RESULT_SUCCESS:
03283       return(CLI_SUCCESS);
03284        case RESULT_SHOWUSAGE:
03285       return(CLI_SHOWUSAGE);
03286        default:
03287       return(CLI_FAILURE);
03288    }
03289 }
03290 
03291 static char *handle_cli_debug(struct ast_cli_entry *e,
03292    int cmd, struct ast_cli_args *a)
03293 {
03294         switch (cmd) {
03295         case CLI_INIT:
03296                 e->command = "rpt debug level";
03297                 e->usage = debug_usage;
03298                 return NULL;
03299         case CLI_GENERATE:
03300                 return NULL;
03301    }
03302    return res2cli(rpt_do_debug(a->fd,a->argc,a->argv));
03303 }
03304 
03305 static char *handle_cli_dump(struct ast_cli_entry *e,
03306    int cmd, struct ast_cli_args *a)
03307 {
03308         switch (cmd) {
03309         case CLI_INIT:
03310                 e->command = "rpt dump level";
03311                 e->usage = dump_usage;
03312                 return NULL;
03313         case CLI_GENERATE:
03314                 return NULL;
03315    }
03316    return res2cli(rpt_do_dump(a->fd,a->argc,a->argv));
03317 }
03318 
03319 
03320 static char *handle_cli_stats(struct ast_cli_entry *e,
03321    int cmd, struct ast_cli_args *a)
03322 {
03323         switch (cmd) {
03324         case CLI_INIT:
03325                 e->command = "rpt stats";
03326                 e->usage = dump_stats;
03327                 return NULL;
03328         case CLI_GENERATE:
03329                 return NULL;
03330    }
03331    return res2cli(rpt_do_stats(a->fd,a->argc,a->argv));
03332 }
03333 
03334 static char *handle_cli_nodes(struct ast_cli_entry *e,
03335    int cmd, struct ast_cli_args *a)
03336 {
03337         switch (cmd) {
03338         case CLI_INIT:
03339                 e->command = "rpt nodes";
03340                 e->usage = dump_nodes;
03341                 return NULL;
03342         case CLI_GENERATE:
03343                 return NULL;
03344    }
03345    return res2cli(rpt_do_nodes(a->fd,a->argc,a->argv));
03346 }
03347 
03348 static char *handle_cli_local_nodes(struct ast_cli_entry *e,
03349    int cmd, struct ast_cli_args *a)
03350 {
03351         switch (cmd) {
03352         case CLI_INIT:
03353                 e->command = "rpt localnodes";
03354                 e->usage = usage_local_nodes;
03355                 return NULL;
03356         case CLI_GENERATE:
03357                 return NULL;
03358    }
03359    return res2cli(rpt_do_local_nodes(a->fd,a->argc,a->argv));
03360 }
03361 
03362 static char *handle_cli_lstats(struct ast_cli_entry *e,
03363    int cmd, struct ast_cli_args *a)
03364 {
03365         switch (cmd) {
03366         case CLI_INIT:
03367                 e->command = "rpt lstats";
03368                 e->usage = dump_lstats;
03369                 return NULL;
03370         case CLI_GENERATE:
03371                 return NULL;
03372    }
03373    return res2cli(rpt_do_lstats(a->fd,a->argc,a->argv));
03374 }
03375 
03376 static char *handle_cli_reload(struct ast_cli_entry *e,
03377    int cmd, struct ast_cli_args *a)
03378 {
03379         switch (cmd) {
03380         case CLI_INIT:
03381                 e->command = "rpt reload";
03382                 e->usage = reload_usage;
03383                 return NULL;
03384         case CLI_GENERATE:
03385                 return NULL;
03386    }
03387    return res2cli(rpt_do_reload(a->fd,a->argc,a->argv));
03388 }
03389 
03390 static char *handle_cli_restart(struct ast_cli_entry *e,
03391    int cmd, struct ast_cli_args *a)
03392 {
03393         switch (cmd) {
03394         case CLI_INIT:
03395                 e->command = "rpt restart";
03396                 e->usage = restart_usage;
03397                 return NULL;
03398         case CLI_GENERATE:
03399                 return NULL;
03400    }
03401    return res2cli(rpt_do_restart(a->fd,a->argc,a->argv));
03402 }
03403 
03404 static char *handle_cli_fun(struct ast_cli_entry *e,
03405    int cmd, struct ast_cli_args *a)
03406 {
03407         switch (cmd) {
03408         case CLI_INIT:
03409                 e->command = "rpt fun";
03410                 e->usage = fun_usage;
03411                 return NULL;
03412         case CLI_GENERATE:
03413                 return NULL;
03414    }
03415    return res2cli(rpt_do_fun(a->fd,a->argc,a->argv));
03416 }
03417 
03418 static char *handle_cli_fun1(struct ast_cli_entry *e,
03419    int cmd, struct ast_cli_args *a)
03420 {
03421         switch (cmd) {
03422         case CLI_INIT:
03423                 e->command = "rpt fun1";
03424                 e->usage = fun_usage;
03425                 return NULL;
03426         case CLI_GENERATE:
03427                 return NULL;
03428    }
03429    return res2cli(rpt_do_fun1(a->fd,a->argc,a->argv));
03430 }
03431 
03432 static char *handle_cli_cmd(struct ast_cli_entry *e,
03433    int cmd, struct ast_cli_args *a)
03434 {
03435         switch (cmd) {
03436         case CLI_INIT:
03437                 e->command = "rpt cmd";
03438                 e->usage = cmd_usage;
03439                 return NULL;
03440         case CLI_GENERATE:
03441                 return NULL;
03442    }
03443    return res2cli(rpt_do_cmd(a->fd,a->argc,a->argv));
03444 }
03445 
03446 static struct ast_cli_entry rpt_cli[] = {
03447    AST_CLI_DEFINE(handle_cli_debug,"Enable app_rpt debugging"),
03448    AST_CLI_DEFINE(handle_cli_dump,"Dump app_rpt structs for debugging"),
03449    AST_CLI_DEFINE(handle_cli_stats,"Dump node statistics"),
03450    AST_CLI_DEFINE(handle_cli_nodes,"Dump node list"),
03451    AST_CLI_DEFINE(handle_cli_local_nodes, "Dump list of local node numbers"),
03452    AST_CLI_DEFINE(handle_cli_lstats,"Dump link statistics"),
03453    AST_CLI_DEFINE(handle_cli_reload,"Reload app_rpt config"),
03454    AST_CLI_DEFINE(handle_cli_restart,"Restart app_rpt"),
03455    AST_CLI_DEFINE(handle_cli_fun,"Execute a DTMF function"),
03456    AST_CLI_DEFINE(handle_cli_fun1,"Execute a DTMF function"),
03457    AST_CLI_DEFINE(handle_cli_cmd,"Execute a DTMF function")
03458 };
03459 
03460 #endif
03461 
03462 static int send_morse(struct ast_channel *chan, char *string, int speed, int freq, int amplitude)
03463 {
03464 
03465 static struct morse_bits mbits[] = {
03466       {0, 0}, /* SPACE */
03467       {0, 0}, 
03468       {6, 18},/* " */
03469       {0, 0},
03470       {7, 72},/* $ */
03471       {0, 0},
03472       {0, 0},
03473       {6, 30},/* ' */
03474       {5, 13},/* ( */
03475       {6, 29},/* ) */
03476       {0, 0},
03477       {5, 10},/* + */
03478       {6, 51},/* , */
03479       {6, 33},/* - */
03480       {6, 42},/* . */
03481       {5, 9}, /* / */
03482       {5, 31},/* 0 */
03483       {5, 30},/* 1 */
03484       {5, 28},/* 2 */
03485       {5, 24},/* 3 */
03486       {5, 16},/* 4 */
03487       {5, 0}, /* 5 */
03488       {5, 1}, /* 6 */
03489       {5, 3}, /* 7 */
03490       {5, 7}, /* 8 */
03491       {5, 15},/* 9 */
03492       {6, 7}, /* : */
03493       {6, 21},/* ; */
03494       {0, 0},
03495       {5, 33},/* = */
03496       {0, 0},
03497       {6, 12},/* ? */
03498       {0, 0},
03499          {2, 2}, /* A */
03500       {4, 1}, /* B */
03501       {4, 5}, /* C */
03502       {3, 1}, /* D */
03503       {1, 0}, /* E */
03504       {4, 4}, /* F */
03505       {3, 3}, /* G */
03506       {4, 0}, /* H */
03507       {2, 0}, /* I */
03508       {4, 14},/* J */
03509       {3, 5}, /* K */
03510       {4, 2}, /* L */
03511       {2, 3}, /* M */
03512       {2, 1}, /* N */
03513       {3, 7}, /* O */
03514       {4, 6}, /* P */
03515       {4, 11},/* Q */
03516       {3, 2}, /* R */
03517       {3, 0}, /* S */
03518       {1, 1}, /* T */
03519       {3, 4}, /* U */
03520       {4, 8}, /* V */
03521       {3, 6}, /* W */
03522       {4, 9}, /* X */
03523       {4, 13},/* Y */
03524       {4, 3}  /* Z */
03525    };
03526 
03527 
03528    int dottime;
03529    int dashtime;
03530    int intralettertime;
03531    int interlettertime;
03532    int interwordtime;
03533    int len, ddcomb;
03534    int res;
03535    int c;
03536    int i;
03537    int flags;
03538          
03539    res = 0;
03540    
03541    /* Approximate the dot time from the speed arg. */
03542    
03543    dottime = 900/speed;
03544    
03545    /* Establish timing releationships */
03546    
03547    dashtime = 3 * dottime;
03548    intralettertime = dottime;
03549    interlettertime = dottime * 4 ;
03550    interwordtime = dottime * 7;
03551    
03552    for(;(*string) && (!res); string++){
03553    
03554       c = *string;
03555       
03556       /* Convert lower case to upper case */
03557       
03558       if((c >= 'a') && (c <= 'z'))
03559          c -= 0x20;
03560       
03561       /* Can't deal with any char code greater than Z, skip it */
03562       
03563       if(c  > 'Z')
03564          continue;
03565       
03566       /* If space char, wait the inter word time */
03567                
03568       if(c == ' '){
03569          if(!res)
03570             res = play_silence(chan, interwordtime);
03571          continue;
03572       }
03573       
03574       /* Subtract out control char offset to match our table */
03575       
03576       c -= 0x20;
03577       
03578       /* Get the character data */
03579       
03580       len = mbits[c].len;
03581       ddcomb = mbits[c].ddcomb;
03582       
03583       /* Send the character */
03584       
03585       for(; len ; len--){
03586          if(!res)
03587             res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
03588          if(!res)
03589             res = play_silence(chan, intralettertime);
03590          ddcomb >>= 1;
03591       }
03592       
03593       /* Wait the interletter time */
03594       
03595       if(!res)
03596          res = play_silence(chan, interlettertime - intralettertime);
03597    }
03598    
03599    /* Wait for all the frames to be sent */
03600    
03601    if (!res) 
03602       res = ast_waitstream(chan, "");
03603    ast_stopstream(chan);
03604    
03605    /*
03606    * Wait for the DAHDI driver to physically write the tone blocks to the hardware
03607    */
03608 
03609    for(i = 0; i < 20 ; i++){
03610       flags =  DAHDI_IOMUX_WRITEEMPTY | DAHDI_IOMUX_NOWAIT; 
03611       res = ioctl(chan->fds[0], DAHDI_IOMUX, &flags);
03612       if(flags & DAHDI_IOMUX_WRITEEMPTY)
03613          break;
03614       if( ast_safe_sleep(chan, 50)){
03615          res = -1;
03616          break;
03617       }
03618    }
03619 
03620    
03621    return res;
03622 }
03623 
03624 static int send_tone_telemetry(struct ast_channel *chan, char *tonestring)
03625 {
03626    char *p,*stringp;
03627    char *tonesubset;
03628    int f1,f2;
03629    int duration;
03630    int amplitude;
03631    int res;
03632    int i;
03633    int flags;
03634    
03635    res = 0;
03636 
03637    if(!tonestring)
03638       return res;
03639    
03640    p = stringp = ast_strdup(tonestring);
03641 
03642    for(;tonestring;){
03643       tonesubset = strsep(&stringp,")");
03644       if(!tonesubset)
03645          break;
03646       if(sscanf(tonesubset,"(%30d,%30d,%30d,%30d", &f1, &f2, &duration, &amplitude) != 4)
03647          break;
03648       res = play_tone_pair(chan, f1, f2, duration, amplitude);
03649       if(res)
03650          break;
03651    }
03652    if(p)
03653       ast_free(p);
03654    if(!res)
03655       res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
03656    
03657    if (!res) 
03658       res = ast_waitstream(chan, "");
03659 
03660    ast_stopstream(chan);
03661 
03662    /*
03663    * Wait for the DAHDI driver to physically write the tone blocks to the hardware
03664    */
03665 
03666    for(i = 0; i < 20 ; i++){
03667       flags =  DAHDI_IOMUX_WRITEEMPTY | DAHDI_IOMUX_NOWAIT; 
03668       res = ioctl(chan->fds[0], DAHDI_IOMUX, &flags);
03669       if(flags & DAHDI_IOMUX_WRITEEMPTY)
03670          break;
03671       if( ast_safe_sleep(chan, 50)){
03672          res = -1;
03673          break;
03674       }
03675    }
03676       
03677    return res;
03678       
03679 }
03680 
03681 static int sayfile(struct ast_channel *mychannel,char *fname)
03682 {
03683 int   res;
03684 
03685    res = ast_streamfile(mychannel, fname, mychannel->language);
03686    if (!res) 
03687       res = ast_waitstream(mychannel, "");
03688    else
03689        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
03690    ast_stopstream(mychannel);
03691    return res;
03692 }
03693 
03694 static int saycharstr(struct ast_channel *mychannel,char *str)
03695 {
03696 int   res;
03697 
03698    res = ast_say_character_str(mychannel,str,NULL,mychannel->language);
03699    if (!res) 
03700       res = ast_waitstream(mychannel, "");
03701    else
03702        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
03703    ast_stopstream(mychannel);
03704    return res;
03705 }
03706 
03707 static int saynum(struct ast_channel *mychannel, int num)
03708 {
03709    int res;
03710    res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
03711    if(!res)
03712       res = ast_waitstream(mychannel, "");
03713    else
03714       ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
03715    ast_stopstream(mychannel);
03716    return res;
03717 }
03718 
03719 /* say a node and nodename. Try to look in dir referred to by nodenames in
03720 config, and see if there's a custom node file to play, and if so, play it */
03721 
03722 static int saynode(struct rpt *myrpt, struct ast_channel *mychannel, char *name)
03723 {
03724 int   res;
03725 char  *val,fname[300];
03726 
03727    val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->name, "nodenames");
03728    if (!val) val = NODENAMES;
03729    snprintf(fname,sizeof(fname) - 1,"%s/%s",val,name);
03730    if (ast_fileexists(fname,NULL,mychannel->language) > 0)
03731       return(sayfile(mychannel,fname));
03732    res = sayfile(mychannel,"rpt/node");
03733    if (!res) 
03734       res = ast_say_character_str(mychannel,name,NULL,mychannel->language);
03735    return res;
03736 }
03737 
03738 static int telem_any(struct rpt *myrpt,struct ast_channel *chan, char *entry)
03739 {
03740    int res;
03741    char c;
03742    
03743    static int morsespeed;
03744    static int morsefreq;
03745    static int morseampl;
03746    static int morseidfreq = 0;
03747    static int morseidampl;
03748    static char mcat[] = MORSE;
03749    
03750    res = 0;
03751    
03752    if(!morseidfreq){ /* Get the morse parameters if not already loaded */
03753       morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
03754          morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
03755          morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
03756       morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
03757       morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330); 
03758    }
03759    
03760    /* Is it a file, or a tone sequence? */
03761          
03762    if(entry[0] == '|'){
03763       c = entry[1];
03764       if((c >= 'a')&&(c <= 'z'))
03765          c -= 0x20;
03766    
03767       switch(c){
03768          case 'I': /* Morse ID */
03769             res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
03770             break;
03771          
03772          case 'M': /* Morse Message */
03773             res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
03774             break;
03775          
03776          case 'T': /* Tone sequence */
03777             res = send_tone_telemetry(chan, entry + 2);
03778             break;
03779          default:
03780             res = -1;
03781       }
03782    }
03783    else
03784       res = sayfile(chan, entry); /* File */
03785    return res;
03786 }
03787 
03788 /*
03789 * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
03790 *
03791 * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
03792 */
03793 
03794 static int telem_lookup(struct rpt *myrpt,struct ast_channel *chan, char *node, char *name)
03795 {
03796    
03797    int res;
03798    int i;
03799    char *entry;
03800    char *telemetry;
03801    char *telemetry_save;
03802 
03803    res = 0;
03804    telemetry_save = NULL;
03805    entry = NULL;
03806    
03807    /* Retrieve the section name for telemetry from the node section */
03808    telemetry = (char *) ast_variable_retrieve(myrpt->cfg, node, TELEMETRY);
03809    if(telemetry ){
03810       telemetry_save = ast_strdup(telemetry);
03811       if(!telemetry_save){
03812          ast_log(LOG_WARNING,"ast_strdup() failed in telem_lookup()\n");
03813          return res;
03814       }
03815       entry = (char *) ast_variable_retrieve(myrpt->cfg, telemetry_save, name);
03816    }
03817    
03818    /* Try to look up the telemetry name */   
03819 
03820    if(!entry){
03821       /* Telemetry name wasn't found in the config file, use the default */
03822       for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
03823          if(!strcasecmp(tele_defs[i].name, name))
03824             entry = tele_defs[i].value;
03825       }
03826    }
03827    if(entry){  
03828       if(strlen(entry))
03829          if (chan) telem_any(myrpt,chan, entry);
03830    }
03831    else{
03832       res = -1;
03833    }
03834    if(telemetry_save)
03835       ast_free(telemetry_save);
03836    return res;
03837 }
03838 
03839 /*
03840 * Retrieve a wait interval
03841 */
03842 
03843 static int get_wait_interval(struct rpt *myrpt, int type)
03844 {
03845         int interval;
03846         char *wait_times;
03847         char *wait_times_save;
03848                                                                                                                   
03849         wait_times_save = NULL;
03850         wait_times = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
03851                                                                                                                   
03852         if(wait_times){
03853                 wait_times_save = ast_strdup(wait_times);
03854                 if(!wait_times_save)
03855          return 0;
03856                 
03857         }
03858                                                                                                                   
03859         switch(type){
03860                 case DLY_TELEM:
03861                         if(wait_times)
03862                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "telemwait", 500, 5000, 1000);
03863                         else
03864                                 interval = 1000;
03865                         break;
03866                                                                                                                   
03867                 case DLY_ID:
03868                         if(wait_times)
03869                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "idwait",250,5000,500);
03870                         else
03871                                 interval = 500;
03872                         break;
03873                                                                                                                   
03874                 case DLY_UNKEY:
03875                         if(wait_times)
03876                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "unkeywait",50,5000,1000);
03877                         else
03878                                 interval = 1000;
03879                         break;
03880                                                                                                                   
03881                 case DLY_LINKUNKEY:
03882                         if(wait_times)
03883                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "linkunkeywait",500,5000,1000);
03884                         else
03885                                 interval = 1000;
03886                         break;
03887                                                                                                                   
03888                 case DLY_CALLTERM:
03889                         if(wait_times)
03890                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "calltermwait",500,5000,1500);
03891                         else
03892                                 interval = 1500;
03893                         break;
03894                                                                                                                   
03895                 case DLY_COMP:
03896                         if(wait_times)
03897                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "compwait",500,5000,200);
03898                         else
03899                                 interval = 200;
03900                         break;
03901                                                                                                                   
03902                 case DLY_PARROT:
03903                         if(wait_times)
03904                                 interval = retrieve_astcfgint(myrpt,wait_times_save, "parrotwait",500,5000,200);
03905                         else
03906                                 interval = 200;
03907                         break;
03908                                                                                                                   
03909                 default:
03910          interval = 0;
03911          break;
03912         }
03913    if(wait_times_save)
03914             ast_free(wait_times_save);
03915    return interval;
03916 }                                                                                                                  
03917 
03918 
03919 /*
03920 * Wait a configurable interval of time 
03921 */
03922 static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
03923 {
03924    int interval;
03925    interval = get_wait_interval(myrpt, type);
03926    if(debug)
03927       ast_log(LOG_NOTICE,"Delay interval = %d\n", interval);
03928    if(interval)
03929       ast_safe_sleep(chan,interval);
03930    if(debug)
03931       ast_log(LOG_NOTICE,"Delay complete\n");
03932    return;
03933 }
03934 
03935 static int split_freq(char *mhz, char *decimals, char *freq);
03936 
03937 static void *rpt_tele_thread(void *this)
03938 {
03939 struct dahdi_confinfo ci;  /* conference info */
03940 int   res = 0,haslink,hastx,hasremote,imdone = 0, unkeys_queued, x;
03941 struct   rpt_tele *mytele = (struct rpt_tele *)this;
03942 struct  rpt_tele *tlist;
03943 struct   rpt *myrpt;
03944 struct   rpt_link *l,*l1,linkbase;
03945 struct   ast_channel *mychannel;
03946 int vmajor, vminor, m;
03947 char *p,*ct,*ct_copy,*ident, *nodename,*cp;
03948 time_t t;
03949 #ifdef   NEW_ASTERISK
03950 struct ast_tm localtm;
03951 #else
03952 struct tm localtm;
03953 #endif
03954 char lbuf[MAXLINKLIST],*strs[MAXLINKLIST];
03955 int   i,ns,rbimode;
03956 char mhz[MAXREMSTR];
03957 char decimals[MAXREMSTR];
03958 char  mystr[200];
03959 struct dahdi_params par;
03960 
03961 
03962    /* get a pointer to myrpt */
03963    myrpt = mytele->rpt;
03964 
03965    /* Snag copies of a few key myrpt variables */
03966    rpt_mutex_lock(&myrpt->lock);
03967    nodename = ast_strdup(myrpt->name);
03968    if(!nodename)
03969    {
03970        fprintf(stderr,"rpt:Sorry unable strdup nodename\n");
03971        rpt_mutex_lock(&myrpt->lock);
03972        remque((struct qelem *)mytele);
03973        ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
03974        rpt_mutex_unlock(&myrpt->lock);
03975        ast_free(mytele);
03976        pthread_exit(NULL);
03977    }
03978 
03979    if (myrpt->p.ident){
03980       ident = ast_strdup(myrpt->p.ident);
03981          if(!ident)
03982       {
03983                  fprintf(stderr,"rpt:Sorry unable strdup ident\n");
03984          rpt_mutex_lock(&myrpt->lock);
03985                   remque((struct qelem *)mytele);
03986                   ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",
03987          __LINE__, mytele->mode); /*@@@@@@@@@@@*/
03988                   rpt_mutex_unlock(&myrpt->lock);
03989          ast_free(nodename);
03990                   ast_free(mytele);
03991                   pthread_exit(NULL);
03992          }
03993    }
03994    else
03995    {
03996       ident = "";
03997    }
03998    rpt_mutex_unlock(&myrpt->lock);
03999       
04000 
04001 
04002    /* allocate a pseudo-channel thru asterisk */
04003    mychannel = ast_request("DAHDI",AST_FORMAT_SLINEAR,"pseudo",NULL);
04004    if (!mychannel)
04005    {
04006       fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
04007       rpt_mutex_lock(&myrpt->lock);
04008       remque((struct qelem *)mytele);
04009       ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
04010       rpt_mutex_unlock(&myrpt->lock);
04011       ast_free(nodename);
04012       ast_free(ident);
04013       ast_free(mytele);    
04014       pthread_exit(NULL);
04015    }
04016 #ifdef   AST_CDR_FLAG_POST_DISABLED
04017    if (mychannel->cdr) 
04018       ast_set_flag(mychannel->cdr,AST_CDR_FLAG_POST_DISABLED);
04019 #endif
04020    rpt_mutex_lock(&myrpt->lock);
04021    mytele->chan = mychannel;
04022    rpt_mutex_unlock(&myrpt->lock);
04023 
04024    while((mytele->mode != SETREMOTE) && (mytele->mode != UNKEY) &&
04025       (mytele->mode != LINKUNKEY))
04026    {  
04027                 rpt_mutex_lock(&myrpt->lock);
04028       if (!myrpt->active_telem)
04029       {
04030          myrpt->active_telem = mytele;
04031                    rpt_mutex_unlock(&myrpt->lock);
04032          break;
04033       }
04034                 rpt_mutex_unlock(&myrpt->lock);
04035       usleep(100000);
04036    }
04037 
04038    /* make a conference for the tx */
04039    ci.chan = 0;
04040    /* If the telemetry is only intended for a local audience, */
04041    /* only connect the ID audio to the local tx conference so */
04042    /* linked systems can't hear it */
04043    ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY) || 
04044       (mytele->mode == TAILMSG) || (mytele->mode == LINKUNKEY) || (mytele->mode == TIMEOUT) || 
04045       (mytele->mode == PARROT) || (mytele->mode == STATS_TIME_LOCAL)) ? 
04046          myrpt->txconf : myrpt->conf);
04047    ci.confmode = DAHDI_CONF_CONFANN;
04048    /* first put the channel on the conference in announce mode */
04049    if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1)
04050    {
04051       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
04052       rpt_mutex_lock(&myrpt->lock);
04053       myrpt->active_telem = NULL;
04054       remque((struct qelem *)mytele);
04055       rpt_mutex_unlock(&myrpt->lock);
04056       ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
04057       ast_free(nodename);
04058       ast_free(ident);
04059       ast_free(mytele);    
04060       ast_hangup(mychannel);
04061       pthread_exit(NULL);
04062    }
04063    ast_stopstream(mychannel);
04064    switch(mytele->mode)
04065    {
04066        case ID:
04067        case ID1:
04068       /* wait a bit */
04069       wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM,mychannel);
04070       res = telem_any(myrpt,mychannel, ident); 
04071       imdone=1;   
04072       break;
04073       
04074        case TAILMSG:
04075       res = ast_streamfile(mychannel, myrpt->p.tailmessages[myrpt->tailmessagen], mychannel->language); 
04076       break;
04077       
04078        case IDTALKOVER:
04079          p = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "idtalkover");
04080          if(p)
04081          res = telem_any(myrpt,mychannel, p); 
04082       imdone=1;   
04083          break;
04084             
04085        case PROC:
04086       /* wait a little bit longer */
04087       wait_interval(myrpt, DLY_TELEM, mychannel);
04088       res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
04089       if(res < 0){ /* Then default message */
04090          res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
04091       }
04092       break;
04093        case TERM:
04094       /* wait a little bit longer */
04095       wait_interval(myrpt, DLY_CALLTERM, mychannel);
04096       res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
04097       if(res < 0){ /* Then default message */
04098          res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
04099       }
04100       break;
04101        case COMPLETE:
04102       /* wait a little bit */
04103       wait_interval(myrpt, DLY_TELEM, mychannel);
04104       res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04105       break;
04106        case MACRO_NOTFOUND:
04107       /* wait a little bit */
04108       wait_interval(myrpt, DLY_TELEM, mychannel);
04109       res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
04110       break;
04111        case MACRO_BUSY:
04112       /* wait a little bit */
04113       wait_interval(myrpt, DLY_TELEM, mychannel);
04114       res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
04115       break;
04116        case UNKEY:
04117       if(myrpt->patchnoct && myrpt->callmode){ /* If no CT during patch configured, then don't send one */
04118          imdone = 1;
04119          break;
04120       }
04121          
04122       /*
04123       * Reset the Unkey to CT timer
04124       */
04125 
04126       x = get_wait_interval(myrpt, DLY_UNKEY);
04127       rpt_mutex_lock(&myrpt->lock);
04128       myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
04129       rpt_mutex_unlock(&myrpt->lock);
04130 
04131       /*
04132       * If there's one already queued, don't do another
04133       */
04134 
04135       tlist = myrpt->tele.next;
04136       unkeys_queued = 0;
04137                 if (tlist != &myrpt->tele)
04138                 {
04139                         rpt_mutex_lock(&myrpt->lock);
04140                         while(tlist != &myrpt->tele){
04141                                 if (tlist->mode == UNKEY) unkeys_queued++;
04142                                 tlist = tlist->next;
04143                         }
04144                         rpt_mutex_unlock(&myrpt->lock);
04145       }
04146       if( unkeys_queued > 1){
04147          imdone = 1;
04148          break;
04149       }
04150 
04151       /* Wait for the telemetry timer to expire */
04152       /* Periodically check the timer since it can be re-initialized above */
04153       while(myrpt->unkeytocttimer)
04154       {
04155          int ctint;
04156          if(myrpt->unkeytocttimer > 100)
04157             ctint = 100;
04158          else
04159             ctint = myrpt->unkeytocttimer;
04160          ast_safe_sleep(mychannel, ctint);
04161          rpt_mutex_lock(&myrpt->lock);
04162          if(myrpt->unkeytocttimer < ctint)
04163             myrpt->unkeytocttimer = 0;
04164          else
04165             myrpt->unkeytocttimer -= ctint;
04166          rpt_mutex_unlock(&myrpt->lock);
04167       }
04168    
04169       /*
04170       * Now, the carrier on the rptr rx should be gone. 
04171       * If it re-appeared, then forget about sending the CT
04172       */
04173       if(myrpt->keyed){
04174          imdone = 1;
04175          break;
04176       }
04177       
04178       rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */
04179       myrpt->dailykerchunks++;
04180       myrpt->totalkerchunks++;
04181       rpt_mutex_unlock(&myrpt->lock);
04182    
04183       haslink = 0;
04184       hastx = 0;
04185       hasremote = 0;    
04186       l = myrpt->links.next;
04187       if (l != &myrpt->links)
04188       {
04189          rpt_mutex_lock(&myrpt->lock);
04190          while(l != &myrpt->links)
04191          {
04192             if (l->name[0] == '0')
04193             {
04194                l = l->next;
04195                continue;
04196             }
04197             haslink = 1;
04198             if (l->mode) {
04199                hastx++;
04200                if (l->isremote) hasremote++;
04201             }
04202             l = l->next;
04203          }
04204          rpt_mutex_unlock(&myrpt->lock);
04205       }
04206       if (haslink)
04207       {
04208 
04209          res = telem_lookup(myrpt,mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
04210          if(res)
04211             ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
04212          
04213       
04214          /* if in remote cmd mode, indicate it */
04215          if (myrpt->cmdnode[0])
04216          {
04217             ast_safe_sleep(mychannel,200);
04218             res = telem_lookup(myrpt,mychannel, myrpt->name, "cmdmode");
04219             if(res)
04220                ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
04221             ast_stopstream(mychannel);
04222          }
04223       }
04224       else if((ct = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */
04225          ct_copy = ast_strdup(ct);
04226          if(ct_copy)
04227          {
04228             res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy);
04229             ast_free(ct_copy);
04230          }
04231          else
04232             res = -1;
04233          if(res)
04234             ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);     
04235       }  
04236       if (hasremote && (!myrpt->cmdnode[0]))
04237       {
04238          /* set for all to hear */
04239          ci.chan = 0;
04240          ci.confno = myrpt->conf;
04241          ci.confmode = DAHDI_CONF_CONFANN;
04242          /* first put the channel on the conference in announce mode */
04243          if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1)
04244          {
04245             ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
04246             rpt_mutex_lock(&myrpt->lock);
04247             myrpt->active_telem = NULL;
04248             remque((struct qelem *)mytele);
04249             rpt_mutex_unlock(&myrpt->lock);
04250             ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
04251             ast_free(nodename);
04252             ast_free(ident);
04253             ast_free(mytele);    
04254             ast_hangup(mychannel);
04255             pthread_exit(NULL);
04256          }
04257          if((ct = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */
04258             ast_safe_sleep(mychannel,200);
04259             ct_copy = ast_strdup(ct);
04260             if(ct_copy)
04261             {
04262                res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy);
04263                ast_free(ct_copy);
04264             }
04265             else
04266                res = -1;
04267       
04268             if(res)
04269                ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);     
04270          }  
04271       }
04272 #if   defined(_MDC_DECODE_H_) && defined(MDC_SAY_WHEN_DOING_CT)
04273       if (myrpt->lastunit)
04274       {
04275          char mystr[10];
04276 
04277          ast_safe_sleep(mychannel,200);
04278          /* set for all to hear */
04279          ci.chan = 0;
04280          ci.confno = myrpt->txconf;
04281          ci.confmode = DAHDI_CONF_CONFANN;
04282          /* first put the channel on the conference in announce mode */
04283          if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1)
04284          {
04285             ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
04286             rpt_mutex_lock(&myrpt->lock);
04287             myrpt->active_telem = NULL;
04288             remque((struct qelem *)mytele);
04289             rpt_mutex_unlock(&myrpt->lock);
04290             ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
04291             ast_free(nodename);
04292             ast_free(ident);
04293             ast_free(mytele);    
04294             ast_hangup(mychannel);
04295             pthread_exit(NULL);
04296          }
04297          sprintf(mystr,"%04x",myrpt->lastunit);
04298          myrpt->lastunit = 0;
04299          ast_say_character_str(mychannel,mystr,NULL,mychannel->language);
04300          break;
04301       }
04302 #endif
04303       imdone = 1;
04304       break;
04305        case LINKUNKEY:
04306       if(myrpt->patchnoct && myrpt->callmode){ /* If no CT during patch configured, then don't send one */
04307          imdone = 1;
04308          break;
04309       }
04310          
04311       /*
04312       * Reset the Unkey to CT timer
04313       */
04314 
04315       x = get_wait_interval(myrpt, DLY_LINKUNKEY);
04316       mytele->mylink.linkunkeytocttimer = x; /* Must be protected as it is changed below */
04317 
04318       /*
04319       * If there's one already queued, don't do another
04320       */
04321 
04322       tlist = myrpt->tele.next;
04323       unkeys_queued = 0;
04324                 if (tlist != &myrpt->tele)
04325                 {
04326                         rpt_mutex_lock(&myrpt->lock);
04327                         while(tlist != &myrpt->tele){
04328                                 if (tlist->mode == LINKUNKEY) unkeys_queued++;
04329                                 tlist = tlist->next;
04330                         }
04331                         rpt_mutex_unlock(&myrpt->lock);
04332       }
04333       if( unkeys_queued > 1){
04334          imdone = 1;
04335          break;
04336       }
04337 
04338       /* Wait for the telemetry timer to expire */
04339       /* Periodically check the timer since it can be re-initialized above */
04340       while(mytele->mylink.linkunkeytocttimer)
04341       {
04342          int ctint;
04343          if(mytele->mylink.linkunkeytocttimer > 100)
04344             ctint = 100;
04345          else
04346             ctint = mytele->mylink.linkunkeytocttimer;
04347          ast_safe_sleep(mychannel, ctint);
04348          rpt_mutex_lock(&myrpt->lock);
04349          if(mytele->mylink.linkunkeytocttimer < ctint)
04350             mytele->mylink.linkunkeytocttimer = 0;
04351          else
04352             mytele->mylink.linkunkeytocttimer -= ctint;
04353          rpt_mutex_unlock(&myrpt->lock);
04354       }
04355    
04356       if((ct = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "linkunkeyct"))){ /* Unlinked Courtesy Tone */
04357          ct_copy = ast_strdup(ct);
04358          if(ct_copy){
04359             res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy);
04360             ast_free(ct_copy);
04361          }
04362          else
04363             res = -1;
04364          if(res)
04365             ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);     
04366       }  
04367       imdone = 1;
04368       break;
04369        case REMDISC:
04370       /* wait a little bit */
04371       wait_interval(myrpt, DLY_TELEM, mychannel);
04372       l = myrpt->links.next;
04373       haslink = 0;
04374       /* dont report if a link for this one still on system */
04375       if (l != &myrpt->links)
04376       {
04377          rpt_mutex_lock(&myrpt->lock);
04378          while(l != &myrpt->links)
04379          {
04380             if (l->name[0] == '0')
04381             {
04382                l = l->next;
04383                continue;
04384             }
04385             if (!strcmp(l->name,mytele->mylink.name))
04386             {
04387                haslink = 1;
04388                break;
04389             }
04390             l = l->next;
04391          }
04392          rpt_mutex_unlock(&myrpt->lock);
04393       }
04394       if (haslink)
04395       {
04396          imdone = 1;
04397          break;
04398       }
04399       res = saynode(myrpt,mychannel,mytele->mylink.name);
04400       if (!res) 
04401           res = ast_streamfile(mychannel, ((mytele->mylink.hasconnected) ? 
04402          "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
04403       break;
04404        case REMALREADY:
04405       /* wait a little bit */
04406       wait_interval(myrpt, DLY_TELEM, mychannel);
04407       res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
04408       break;
04409        case REMNOTFOUND:
04410       /* wait a little bit */
04411       wait_interval(myrpt, DLY_TELEM, mychannel);
04412       res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
04413       break;
04414        case REMGO:
04415       /* wait a little bit */
04416       wait_interval(myrpt, DLY_TELEM, mychannel);
04417       res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
04418       break;
04419        case CONNECTED:
04420       /* wait a little bit */
04421       wait_interval(myrpt, DLY_TELEM,  mychannel);
04422       res = saynode(myrpt,mychannel,mytele->mylink.name);
04423       if (!res)
04424           res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
04425       if (!res) 
04426          res = ast_waitstream(mychannel, "");
04427       else
04428           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04429       ast_stopstream(mychannel);
04430       res = ast_streamfile(mychannel, "digits/2", mychannel->language);
04431       if (!res) 
04432          res = ast_waitstream(mychannel, "");
04433       else
04434           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04435       ast_stopstream(mychannel);
04436       res = saynode(myrpt,mychannel,myrpt->name);
04437       imdone = 1;
04438       break;
04439        case CONNFAIL:
04440       res = saynode(myrpt,mychannel,mytele->mylink.name);
04441       if (!res) 
04442           res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
04443       break;
04444        case MEMNOTFOUND:
04445       /* wait a little bit */
04446       wait_interval(myrpt, DLY_TELEM, mychannel);
04447       res = ast_streamfile(mychannel, "rpt/memory_notfound", mychannel->language);
04448       break;
04449        case PLAYBACK:
04450       /* wait a little bit */
04451       wait_interval(myrpt, DLY_TELEM, mychannel);
04452       res = ast_streamfile(mychannel, mytele->param, mychannel->language);
04453       break;
04454        case TOPKEY:
04455       /* wait a little bit */
04456       wait_interval(myrpt, DLY_TELEM, mychannel);
04457       for(i = 0; i < TOPKEYN; i++)
04458       {
04459          if (!myrpt->topkey[i].node[0]) continue;
04460          if ((!myrpt->topkeylong) && (myrpt->topkey[i].keyed)) continue;
04461          res = saynode(myrpt, mychannel,  myrpt->topkey[i].node);
04462          if (!res) res = sayfile(mychannel,(myrpt->topkey[i].keyed) ?
04463             "rpt/keyedfor" : "rpt/unkeyedfor");
04464          if (!res) res = saynum(mychannel,
04465             myrpt->topkey[i].timesince);
04466          if (!res) res = sayfile(mychannel,"rpt/seconds");
04467          if (!myrpt->topkeylong) break;
04468       }
04469       imdone = 1;
04470       break;
04471        case SETREMOTE:
04472       ast_mutex_lock(&myrpt->remlock);
04473       res = 0;
04474       if(!strcmp(myrpt->remoterig, remote_rig_ft897))
04475       {
04476          res = set_ft897(myrpt);
04477       }
04478       else if(!strcmp(myrpt->remoterig, remote_rig_tm271))
04479       {
04480          res = set_tm271(myrpt);
04481       }
04482       else if(!strcmp(myrpt->remoterig, remote_rig_ic706))
04483       {
04484          res = set_ic706(myrpt);
04485       }
04486 #ifdef HAVE_IOPERM
04487       else if(!strcmp(myrpt->remoterig, remote_rig_rbi)||!strcmp(myrpt->remoterig, remote_rig_ppp16))
04488       {
04489          if (ioperm(myrpt->p.iobase,1,1) == -1)
04490          {
04491             rpt_mutex_unlock(&myrpt->lock);
04492             ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->p.iobase);
04493             res = -1;
04494          }
04495          else res = setrbi(myrpt);
04496       }
04497 #endif
04498       else if(!strcmp(myrpt->remoterig, remote_rig_kenwood))
04499       {
04500          if (myrpt->iofd >= 0) setdtr(myrpt->iofd,1);
04501          res = setkenwood(myrpt);
04502          if (myrpt->iofd >= 0) setdtr(myrpt->iofd,0);
04503          if (ast_safe_sleep(mychannel,200) == -1)
04504          {
04505             ast_mutex_unlock(&myrpt->remlock);
04506             res = -1;
04507             break;
04508          }
04509          if (myrpt->iofd < 0)
04510          {
04511             i = DAHDI_FLUSH_EVENT;
04512             if (ioctl(myrpt->dahditxchannel->fds[0],DAHDI_FLUSH,&i) == -1)
04513             {
04514                ast_mutex_unlock(&myrpt->remlock);
04515                ast_log(LOG_ERROR,"Cant flush events");
04516                res = -1;
04517                break;
04518             }
04519             if (ioctl(myrpt->dahdirxchannel->fds[0],DAHDI_GET_PARAMS,&par) == -1)
04520             {
04521                ast_mutex_unlock(&myrpt->remlock);
04522                ast_log(LOG_ERROR,"Cant get params");
04523                res = -1;
04524                break;
04525             }
04526             myrpt->remoterx = 
04527                (par.rxisoffhook || (myrpt->tele.next != &myrpt->tele));
04528          }
04529       }
04530 
04531       ast_mutex_unlock(&myrpt->remlock);
04532       if (!res)
04533       {
04534          imdone = 1;
04535          break;
04536       }
04537       /* fall thru to invalid freq */
04538        case INVFREQ:
04539       /* wait a little bit */
04540       wait_interval(myrpt, DLY_TELEM, mychannel);
04541       res = ast_streamfile(mychannel, "rpt/invalid-freq", mychannel->language);
04542       break;
04543        case REMMODE:
04544       cp = 0;
04545       wait_interval(myrpt, DLY_TELEM, mychannel);
04546       switch(myrpt->remmode)
04547       {
04548           case REM_MODE_FM:
04549          saycharstr(mychannel,"FM");
04550          break;
04551           case REM_MODE_USB:
04552          saycharstr(mychannel,"USB");
04553          break;
04554           case REM_MODE_LSB:
04555          saycharstr(mychannel,"LSB");
04556          break;
04557           case REM_MODE_AM:
04558          saycharstr(mychannel,"AM");
04559          break;
04560       }
04561       wait_interval(myrpt, DLY_COMP, mychannel);
04562       if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04563       break;
04564        case LOGINREQ:
04565       wait_interval(myrpt, DLY_TELEM, mychannel);
04566       sayfile(mychannel,"rpt/login");
04567       saycharstr(mychannel,myrpt->name);
04568       break;
04569        case REMLOGIN:
04570       wait_interval(myrpt, DLY_TELEM, mychannel);
04571       saycharstr(mychannel,myrpt->loginuser);
04572       saynode(myrpt,mychannel,myrpt->name);
04573       wait_interval(myrpt, DLY_COMP, mychannel);
04574       if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04575       break;
04576        case REMXXX:
04577       wait_interval(myrpt, DLY_TELEM, mychannel);
04578       res = 0;
04579       switch(mytele->submode)
04580       {
04581           case 100: /* RX PL Off */
04582          sayfile(mychannel, "rpt/rxpl");
04583          sayfile(mychannel, "rpt/off");
04584          break;
04585           case 101: /* RX PL On */
04586          sayfile(mychannel, "rpt/rxpl");
04587          sayfile(mychannel, "rpt/on");
04588          break;
04589           case 102: /* TX PL Off */
04590          sayfile(mychannel, "rpt/txpl");
04591          sayfile(mychannel, "rpt/off");
04592          break;
04593           case 103: /* TX PL On */
04594          sayfile(mychannel, "rpt/txpl");
04595          sayfile(mychannel, "rpt/on");
04596          break;
04597           case 104: /* Low Power */
04598          sayfile(mychannel, "rpt/lopwr");
04599          break;
04600           case 105: /* Medium Power */
04601          sayfile(mychannel, "rpt/medpwr");
04602          break;
04603           case 106: /* Hi Power */
04604          sayfile(mychannel, "rpt/hipwr");
04605          break;
04606           case 113: /* Scan down slow */
04607          sayfile(mychannel,"rpt/down");
04608          sayfile(mychannel, "rpt/slow");
04609          break;
04610           case 114: /* Scan down quick */
04611          sayfile(mychannel,"rpt/down");
04612          sayfile(mychannel, "rpt/quick");
04613          break;
04614           case 115: /* Scan down fast */
04615          sayfile(mychannel,"rpt/down");
04616          sayfile(mychannel, "rpt/fast");
04617          break;
04618           case 116: /* Scan up slow */
04619          sayfile(mychannel,"rpt/up");
04620          sayfile(mychannel, "rpt/slow");
04621          break;
04622           case 117: /* Scan up quick */
04623          sayfile(mychannel,"rpt/up");
04624          sayfile(mychannel, "rpt/quick");
04625          break;
04626           case 118: /* Scan up fast */
04627          sayfile(mychannel,"rpt/up");
04628          sayfile(mychannel, "rpt/fast");
04629          break;
04630           default:
04631          res = -1;
04632       }
04633       wait_interval(myrpt, DLY_COMP, mychannel);
04634       if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04635       break;
04636        case SCAN:
04637       ast_mutex_lock(&myrpt->remlock);
04638       if (myrpt->hfscanstop)
04639       {
04640          myrpt->hfscanstatus = 0;
04641          myrpt->hfscanmode = 0;
04642          myrpt->hfscanstop = 0;
04643          mytele->mode = SCANSTAT;
04644          ast_mutex_unlock(&myrpt->remlock);
04645          if (ast_safe_sleep(mychannel,1000) == -1) break;
04646          sayfile(mychannel, "rpt/stop"); 
04647          imdone = 1;
04648          break;
04649       }
04650       if (myrpt->hfscanstatus > -2) service_scan(myrpt);
04651       i = myrpt->hfscanstatus;
04652       myrpt->hfscanstatus = 0;
04653       if (i) mytele->mode = SCANSTAT;
04654       ast_mutex_unlock(&myrpt->remlock);
04655       if (i < 0) sayfile(mychannel, "rpt/stop"); 
04656       else if (i > 0) saynum(mychannel,i);
04657       imdone = 1;
04658       break;
04659        case TUNE:
04660       ast_mutex_lock(&myrpt->remlock);
04661       if (!strcmp(myrpt->remoterig,remote_rig_ic706))
04662       {
04663          set_mode_ic706(myrpt, REM_MODE_AM);
04664          if(play_tone(mychannel, 800, 6000, 8192) == -1) break;
04665          ast_safe_sleep(mychannel,500);
04666          set_mode_ic706(myrpt, myrpt->remmode);
04667          myrpt->tunerequest = 0;
04668          ast_mutex_unlock(&myrpt->remlock);
04669          imdone = 1;
04670          break;
04671       }
04672       set_mode_ft897(myrpt, REM_MODE_AM);
04673       simple_command_ft897(myrpt, 8);
04674       if(play_tone(mychannel, 800, 6000, 8192) == -1) break;
04675       simple_command_ft897(myrpt, 0x88);
04676       ast_safe_sleep(mychannel,500);
04677       set_mode_ft897(myrpt, myrpt->remmode);
04678       myrpt->tunerequest = 0;
04679       ast_mutex_unlock(&myrpt->remlock);
04680       imdone = 1;
04681       break;
04682        case REMSHORTSTATUS:
04683        case REMLONGSTATUS: 
04684       wait_interval(myrpt, DLY_TELEM, mychannel);
04685       res = saynode(myrpt,mychannel,myrpt->name);
04686       if(!res)
04687          res = sayfile(mychannel,"rpt/frequency");
04688       if(!res)
04689          res = split_freq(mhz, decimals, myrpt->freq);
04690       if (!multimode_capable(myrpt)) decimals[3] = 0;
04691       if(!res){
04692          m = atoi(mhz);
04693          if(m < 100)
04694             res = saynum(mychannel, m);
04695          else
04696             res = saycharstr(mychannel, mhz);
04697       }
04698       if(!res)
04699          res = sayfile(mychannel, "letters/dot");
04700       if(!res)
04701          res = saycharstr(mychannel, decimals);
04702    
04703       if(res)  break;
04704       if(myrpt->remmode == REM_MODE_FM){ /* Mode FM? */
04705          switch(myrpt->offset){
04706    
04707             case REM_MINUS:
04708                res = sayfile(mychannel,"rpt/minus");
04709                break;
04710             
04711             case REM_SIMPLEX:
04712                res = sayfile(mychannel,"rpt/simplex");
04713                break;
04714                
04715             case REM_PLUS:
04716                res = sayfile(mychannel,"rpt/plus");
04717                break;
04718                
04719             default:
04720                break;
04721          }
04722       }
04723       else{ /* Must be USB, LSB, or AM */
04724          switch(myrpt->remmode){
04725 
04726             case REM_MODE_USB:
04727                res = saycharstr(mychannel, "USB");
04728                break;
04729 
04730             case REM_MODE_LSB:
04731                res = saycharstr(mychannel, "LSB");
04732                break;
04733 
04734             case REM_MODE_AM:
04735                res = saycharstr(mychannel, "AM");
04736                break;
04737 
04738 
04739             default:
04740                break;
04741          }
04742       }
04743 
04744       if (res == -1) break;
04745 
04746       if(mytele->mode == REMSHORTSTATUS){ /* Short status? */
04747          wait_interval(myrpt, DLY_COMP, mychannel);
04748          if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04749          break;
04750       }
04751 
04752       if (strcmp(myrpt->remoterig,remote_rig_ic706))
04753       {
04754          switch(myrpt->powerlevel){
04755 
04756             case REM_LOWPWR:
04757                res = sayfile(mychannel,"rpt/lopwr") ;
04758                break;
04759             case REM_MEDPWR:
04760                res = sayfile(mychannel,"rpt/medpwr");
04761                break;
04762             case REM_HIPWR:
04763                res = sayfile(mychannel,"rpt/hipwr"); 
04764                break;
04765             }
04766       }
04767 
04768       rbimode = ((!strncmp(myrpt->remoterig,remote_rig_rbi,3))
04769         || (!strncmp(myrpt->remoterig,remote_rig_ic706,3)));
04770       if (res || (sayfile(mychannel,"rpt/rxpl") == -1)) break;
04771       if (rbimode && (sayfile(mychannel,"rpt/txpl") == -1)) break;
04772       if ((sayfile(mychannel,"rpt/frequency") == -1) ||
04773          (saycharstr(mychannel,myrpt->rxpl) == -1)) break;
04774       if ((!rbimode) && ((sayfile(mychannel,"rpt/txpl") == -1) ||
04775          (sayfile(mychannel,"rpt/frequency") == -1) ||
04776          (saycharstr(mychannel,myrpt->txpl) == -1))) break;
04777       if(myrpt->remmode == REM_MODE_FM){ /* Mode FM? */
04778          if ((sayfile(mychannel,"rpt/rxpl") == -1) ||
04779             (sayfile(mychannel,((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1) ||
04780             (sayfile(mychannel,"rpt/txpl") == -1) ||
04781             (sayfile(mychannel,((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1))
04782             {
04783                break;
04784             }
04785       }
04786       wait_interval(myrpt, DLY_COMP, mychannel);
04787       if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
04788       break;
04789        case STATUS:
04790       /* wait a little bit */
04791       wait_interval(myrpt, DLY_TELEM, mychannel);
04792       hastx = 0;
04793       linkbase.next = &linkbase;
04794       linkbase.prev = &linkbase;
04795       rpt_mutex_lock(&myrpt->lock);
04796       /* make our own list of links */
04797       l = myrpt->links.next;
04798       while(l != &myrpt->links)
04799       {
04800          if (l->name[0] == '0')
04801          {
04802             l = l->next;
04803             continue;
04804          }
04805          l1 = ast_malloc(sizeof(struct rpt_link));
04806          if (!l1)
04807          {
04808             ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
04809             remque((struct qelem *)mytele);
04810             myrpt->active_telem = NULL;
04811             rpt_mutex_unlock(&myrpt->lock);
04812             ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
04813             ast_free(nodename);
04814             ast_free(ident);
04815             ast_free(mytele);    
04816             ast_hangup(mychannel);
04817             pthread_exit(NULL);
04818          }
04819          memcpy(l1,l,sizeof(struct rpt_link));
04820          l1->next = l1->prev = NULL;
04821          insque((struct qelem *)l1,(struct qelem *)linkbase.next);
04822          l = l->next;
04823       }
04824       rpt_mutex_unlock(&myrpt->lock);
04825       res = saynode(myrpt,mychannel,myrpt->name);
04826       if (myrpt->callmode)
04827       {
04828          hastx = 1;
04829          res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
04830          if (!res) 
04831             res = ast_waitstream(mychannel, "");
04832          else
04833              ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04834          ast_stopstream(mychannel);
04835       }
04836       l = linkbase.next;
04837       while(l != &linkbase)
04838       {
04839          char *s;
04840 
04841          hastx = 1;
04842          res = saynode(myrpt,mychannel,l->name);
04843          s = "rpt/tranceive";
04844          if (!l->mode) s = "rpt/monitor";
04845          if (!l->thisconnected) s = "rpt/connecting";
04846          res = ast_streamfile(mychannel, s, mychannel->language);
04847          if (!res) 
04848             res = ast_waitstream(mychannel, "");
04849          else
04850             ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04851          ast_stopstream(mychannel);
04852          l = l->next;
04853       }        
04854       if (!hastx)
04855       {
04856          res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
04857          if (!res) 
04858             res = ast_waitstream(mychannel, "");
04859          else
04860              ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04861          ast_stopstream(mychannel);
04862       }
04863       /* destroy our local link queue */
04864       l = linkbase.next;
04865       while(l != &linkbase)
04866       {
04867          l1 = l;
04868          l = l->next;
04869          remque((struct qelem *)l1);
04870          ast_free(l1);
04871       }        
04872       imdone = 1;
04873       break;
04874        case FULLSTATUS:
04875       rpt_mutex_lock(&myrpt->lock);
04876       /* get all the nodes */
04877       __mklinklist(myrpt,NULL,lbuf);
04878       rpt_mutex_unlock(&myrpt->lock);
04879       /* parse em */
04880       ns = finddelim(lbuf,strs,MAXLINKLIST);
04881       /* sort em */
04882       if (ns) qsort((void *)strs,ns,sizeof(char *),mycompar);
04883       /* wait a little bit */
04884       wait_interval(myrpt, DLY_TELEM, mychannel);
04885       hastx = 0;
04886       res = saynode(myrpt,mychannel,myrpt->name);
04887       if (myrpt->callmode)
04888       {
04889          hastx = 1;
04890          res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
04891          if (!res) 
04892             res = ast_waitstream(mychannel, "");
04893          else
04894              ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04895          ast_stopstream(mychannel);
04896       }
04897       /* go thru all the nodes in list */
04898       for(i = 0; i < ns; i++)
04899       {
04900          char *s,mode = 'T';
04901 
04902          /* if a mode spec at first, handle it */
04903          if ((*strs[i] < '0') || (*strs[i] > '9'))
04904          {
04905             mode = *strs[i];
04906             strs[i]++;
04907          }
04908 
04909          hastx = 1;
04910          res = saynode(myrpt,mychannel,strs[i]);
04911          s = "rpt/tranceive";
04912          if (mode == 'R') s = "rpt/monitor";
04913          if (mode == 'C') s = "rpt/connecting";
04914          res = ast_streamfile(mychannel, s, mychannel->language);
04915          if (!res) 
04916             res = ast_waitstream(mychannel, "");
04917          else
04918             ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04919          ast_stopstream(mychannel);
04920       }        
04921       if (!hastx)
04922       {
04923          res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
04924          if (!res) 
04925             res = ast_waitstream(mychannel, "");
04926          else
04927              ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04928          ast_stopstream(mychannel);
04929       }
04930       imdone = 1;
04931       break;
04932 
04933        case LASTNODEKEY: /* Identify last node which keyed us up */
04934       rpt_mutex_lock(&myrpt->lock);
04935       if(myrpt->lastnodewhichkeyedusup){
04936          p = ast_strdup(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */
04937          if(!p){
04938             ast_log(LOG_WARNING, "ast_strdup failed in telemetery LASTNODEKEY");
04939             imdone = 1;
04940             break;
04941          }
04942       }
04943       else
04944          p = NULL;
04945       rpt_mutex_unlock(&myrpt->lock);
04946       if(!p){
04947          imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */
04948          break;
04949       }
04950       wait_interval(myrpt, DLY_TELEM, mychannel);
04951       res = saynode(myrpt,mychannel,p);
04952       ast_free(p);
04953       imdone = 1;
04954       break;      
04955 
04956        case UNAUTHTX: /* Say unauthorized transmit frequency */
04957       wait_interval(myrpt, DLY_TELEM, mychannel);
04958       res = ast_streamfile(mychannel, "rpt/unauthtx", mychannel->language);
04959       if (!res) 
04960          res = ast_waitstream(mychannel, "");
04961       else
04962           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04963       ast_stopstream(mychannel);
04964       imdone = 1;
04965       break;
04966 
04967        case PARROT: /* Repeat stuff */
04968 
04969       sprintf(mystr,PARROTFILE,myrpt->name,(unsigned int)mytele->parrot);
04970       if (ast_fileexists(mystr,NULL,mychannel->language) <= 0)
04971       {
04972          imdone = 1;
04973          myrpt->parrotstate = 0;
04974          break;
04975       }
04976       wait_interval(myrpt, DLY_PARROT, mychannel);
04977       sprintf(mystr,PARROTFILE,myrpt->name,(unsigned int)mytele->parrot);
04978       res = ast_streamfile(mychannel, mystr, mychannel->language);
04979       if (!res) 
04980          res = ast_waitstream(mychannel, "");
04981       else
04982           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
04983       ast_stopstream(mychannel);
04984       sprintf(mystr,PARROTFILE,myrpt->name,(unsigned int)mytele->parrot);
04985       strcat(mystr,".wav");
04986       unlink(mystr);       
04987       imdone = 1;
04988       myrpt->parrotstate = 0;
04989       break;
04990 
04991        case TIMEOUT:
04992       res = saynode(myrpt,mychannel,myrpt->name);
04993       if (!res)
04994          res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language);
04995       break;
04996       
04997        case TIMEOUT_WARNING:
04998       time(&t);
04999       res = saynode(myrpt,mychannel,myrpt->name);
05000       if (!res)
05001          res = ast_streamfile(mychannel, "rpt/timeout-warning", mychannel->language);
05002       if (!res) 
05003          res = ast_waitstream(mychannel, "");
05004       else
05005           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
05006       ast_stopstream(mychannel);
05007       if(!res) /* Say number of seconds */
05008          ast_say_number(mychannel, myrpt->p.remotetimeout - 
05009              (t - myrpt->last_activity_time), 
05010             "", mychannel->language, (char *) NULL);
05011       if (!res) 
05012          res = ast_waitstream(mychannel, "");
05013       ast_stopstream(mychannel); 
05014       res = ast_streamfile(mychannel, "queue-seconds", mychannel->language);
05015       break;
05016 
05017        case ACT_TIMEOUT_WARNING:
05018       time(&t);
05019       res = saynode(myrpt,mychannel,myrpt->name);
05020       if (!res)
05021           res = ast_streamfile(mychannel, "rpt/act-timeout-warning", mychannel->language);
05022       if (!res) 
05023          res = ast_waitstream(mychannel, "");
05024       else
05025           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
05026       ast_stopstream(mychannel);
05027       if(!res) /* Say number of seconds */
05028          ast_say_number(mychannel, myrpt->p.remoteinacttimeout - 
05029              (t - myrpt->last_activity_time), 
05030             "", mychannel->language, (char *) NULL);
05031       if (!res) 
05032          res = ast_waitstream(mychannel, "");
05033       ast_stopstream(mychannel); 
05034       res = ast_streamfile(mychannel, "queue-seconds", mychannel->language);
05035       break;
05036       
05037        case STATS_TIME:
05038             case STATS_TIME_LOCAL:
05039          wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
05040       t = time(NULL);
05041       rpt_localtime(&t, &localtm);
05042       /* Say the phase of the day is before the time */
05043       if((localtm.tm_hour >= 0) && (localtm.tm_hour < 12))
05044          p = "rpt/goodmorning";
05045       else if((localtm.tm_hour >= 12) && (localtm.tm_hour < 18))
05046          p = "rpt/goodafternoon";
05047       else
05048          p = "rpt/goodevening";
05049       if (sayfile(mychannel,p) == -1)
05050       {
05051          imdone = 1;
05052          break;
05053       }
05054       /* Say the time is ... */     
05055       if (sayfile(mychannel,"rpt/thetimeis") == -1)
05056       {
05057          imdone = 1;
05058          break;
05059       }
05060       /* Say the time */            
05061          res = ast_say_time(mychannel, t, "", mychannel->language);
05062       if (!res) 
05063          res = ast_waitstream(mychannel, "");
05064       ast_stopstream(mychannel);    
05065       imdone = 1;
05066          break;
05067        case STATS_VERSION:
05068       p = strstr(tdesc, "version"); 
05069       if(!p)
05070          break;   
05071       if(sscanf(p, "version %30d.%30d", &vmajor, &vminor) != 2)
05072          break;
05073          wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
05074       /* Say "version" */
05075       if (sayfile(mychannel,"rpt/version") == -1)
05076       {
05077          imdone = 1;
05078          break;
05079       }
05080       if(!res) /* Say "X" */
05081          ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL);
05082       if (!res) 
05083          res = ast_waitstream(mychannel, "");
05084       ast_stopstream(mychannel); 
05085       if (saycharstr(mychannel,".") == -1)
05086       {
05087          imdone = 1;
05088          break;
05089       }
05090       if(!res) /* Say "Y" */
05091          ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL);
05092       if (!res){
05093          res = ast_waitstream(mychannel, "");
05094          ast_stopstream(mychannel);
05095       }  
05096       else
05097           ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
05098       imdone = 1;
05099          break;
05100        case ARB_ALPHA:
05101          wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
05102          if(mytele->param)
05103             saycharstr(mychannel, mytele->param);
05104          imdone = 1;
05105       break;
05106        case REV_PATCH:
05107          wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
05108          if(mytele->param) {
05109 
05110          /* Parts of this section taken from app_parkandannounce */
05111          char *tpl_working, *tpl_current;
05112          char *tmp[100], *myparm;
05113          int looptemp=0,idx=0, dres = 0;
05114    
05115 
05116          tpl_working = ast_strdup(mytele->param);
05117          myparm = strsep(&tpl_working,",");
05118          tpl_current=strsep(&tpl_working, ":");
05119 
05120          while(tpl_current && looptemp < sizeof(tmp)) {
05121             tmp[looptemp]=tpl_current;
05122             looptemp++;
05123             tpl_current=strsep(&tpl_working,":");
05124          }
05125 
05126          for(idx=0; idx<looptemp; idx++) {
05127             if(!strcmp(tmp[idx], "PARKED")) {
05128                ast_say_digits(mychannel, atoi(myparm), "", mychannel->language);
05129             } else if(!strcmp(tmp[idx], "NODE")) {
05130                ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language);
05131             } else {
05132                dres = ast_streamfile(mychannel, tmp[idx], mychannel->language);
05133                if(!dres) {
05134                   dres = ast_waitstream(mychannel, "");
05135                } else {
05136                   ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[idx], mychannel->name);
05137                   dres = 0;
05138                }
05139             }
05140          }
05141          ast_free(tpl_working);
05142       }
05143          imdone = 1;
05144       break;
05145        case TEST_TONE:
05146       imdone = 1;
05147       if (myrpt->stopgen) break;
05148       myrpt->stopgen = -1;
05149            if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0))) 
05150       {
05151          myrpt->stopgen = 0;
05152          break;
05153       }
05154            while(mychannel->generatordata && (myrpt->stopgen <= 0)) {
05155          if (ast_safe_sleep(mychannel,1)) break;
05156             imdone = 1;
05157          }
05158       myrpt->stopgen = 0;
05159       break;
05160        default:
05161          break;
05162    }
05163    if (!imdone)
05164    {
05165       if (!res) 
05166          res = ast_waitstream(mychannel, "");
05167       else {
05168          ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
05169          res = 0;
05170       }
05171    }
05172    ast_stopstream(mychannel);
05173    rpt_mutex_lock(&myrpt->lock);
05174    if (mytele->mode == TAILMSG)
05175    {
05176       if (!res)
05177       {
05178          myrpt->tailmessagen++;
05179          if(myrpt->tailmessagen >= myrpt->p.tailmessagemax) myrpt->tailmessagen = 0;
05180       }
05181       else
05182       {
05183          myrpt->tmsgtimer = myrpt->p.tailsquashedtime;
05184       }
05185    }
05186    remque((struct qelem *)mytele);
05187    myrpt->active_telem = NULL;
05188    rpt_mutex_unlock(&myrpt->lock);
05189    ast_free(nodename);
05190    ast_free(ident);
05191    ast_free(mytele);    
05192    ast_hangup(mychannel);
05193 #ifdef  APP_RPT_LOCK_DEBUG
05194    {
05195       struct lockthread *t;
05196 
05197       sleep(5);
05198       ast_mutex_lock(&locklock);
05199       t = get_lockthread(pthread_self());
05200       if (t) memset(t,0,sizeof(struct lockthread));
05201       ast_mutex_unlock(&locklock);
05202    }        
05203 #endif
05204    pthread_exit(NULL);
05205 }
05206 
05207 static void rpt_telemetry(struct rpt *myrpt,int mode, void *data)
05208 {
05209 struct rpt_tele *tele;
05210 struct rpt_link *mylink = NULL;
05211 int res;
05212 pthread_attr_t attr;
05213 char *v1, *v2;
05214 
05215    if(debug > 6)
05216       ast_log(LOG_NOTICE,"mode=%i  data=%s\n",mode, (char *)data);
05217 
05218    switch(mode)
05219    {
05220        case UNKEY:
05221       /* if any of the following are defined, go ahead and do it,
05222          otherwise, dont bother */
05223       v1 = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->name, 
05224          "unlinkedct");
05225       v2 = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->name, 
05226          "remotect");
05227       if (telem_lookup(myrpt,NULL, myrpt->name, "remotemon") &&
05228         telem_lookup(myrpt,NULL, myrpt->name, "remotetx") &&
05229         telem_lookup(myrpt,NULL, myrpt->name, "cmdmode") &&
05230         (!(v1 && telem_lookup(myrpt,NULL, myrpt->name, v1))) && 
05231         (!(v2 && telem_lookup(myrpt,NULL, myrpt->name, v2)))) return;
05232       break;
05233        case LINKUNKEY:
05234       if (!ast_variable_retrieve(myrpt->cfg, myrpt->name, "linkunkeyct"))
05235          return;
05236       break;
05237        default:
05238       break;
05239    }
05240    tele = ast_malloc(sizeof(struct rpt_tele));
05241    if (!tele)
05242    {
05243       ast_log(LOG_WARNING, "Unable to allocate memory\n");
05244       pthread_exit(NULL);
05245       return;
05246    }
05247    /* zero it out */
05248    memset((char *)tele,0,sizeof(struct rpt_tele));
05249    tele->rpt = myrpt;
05250    tele->mode = mode;
05251    if (mode == PARROT) tele->parrot = (uintptr_t) data;
05252    else mylink = (struct rpt_link *) data;
05253    rpt_mutex_lock(&myrpt->lock);
05254    if((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED) ||
05255        (mode == LINKUNKEY)){
05256       memset(&tele->mylink,0,sizeof(struct rpt_link));
05257       if (mylink){
05258          memcpy(&tele->mylink,mylink,sizeof(struct rpt_link));
05259       }
05260    }
05261    else if ((mode == ARB_ALPHA) || (mode == REV_PATCH) || (mode == PLAYBACK)) {
05262       strncpy(tele->param, (char *) data, TELEPARAMSIZE - 1);
05263       tele->param[TELEPARAMSIZE - 1] = 0;
05264    }
05265    if (mode == REMXXX) tele->submode = (intptr_t) data;
05266    insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next);
05267    rpt_mutex_unlock(&myrpt->lock);
05268         pthread_attr_init(&attr);
05269         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
05270    res = ast_pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele);
05271    if(res < 0){
05272       rpt_mutex_lock(&myrpt->lock);
05273       remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */
05274       rpt_mutex_unlock(&myrpt->lock);  
05275       ast_log(LOG_WARNING, "Could not create telemetry thread: %s",strerror(res));
05276    }
05277    return;
05278 }
05279 
05280 static void *rpt_call(void *this)
05281 {
05282 struct dahdi_confinfo ci;  /* conference info */
05283 struct   rpt *myrpt = (struct rpt *)this;
05284 int   res;
05285 int stopped,congstarted,dialtimer,lastcidx,aborted;
05286 struct ast_channel *mychannel,*genchannel;
05287 
05288    myrpt->mydtmf = 0;
05289    /* allocate a pseudo-channel thru asterisk */
05290    mychannel = ast_request("DAHDI",AST_FORMAT_SLINEAR,"pseudo",NULL);
05291    if (!mychannel)
05292    {
05293       fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
05294       pthread_exit(NULL);
05295    }
05296 #ifdef   AST_CDR_FLAG_POST_DISABLED
05297    if (mychannel->cdr)
05298       ast_set_flag(mychannel->cdr,AST_CDR_FLAG_POST_DISABLED);
05299 #endif
05300    ci.chan = 0;
05301    ci.confno = myrpt->conf; /* use the pseudo conference */
05302 #if   0
05303    ci.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER
05304       | DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER; 
05305 #endif
05306    ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
05307    /* first put the channel on the conference */
05308    if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1)
05309    {
05310       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05311       ast_hangup(mychannel);
05312       myrpt->callmode = 0;
05313       pthread_exit(NULL);
05314    }
05315    /* allocate a pseudo-channel thru asterisk */
05316    genchannel = ast_request("DAHDI",AST_FORMAT_SLINEAR,"pseudo",NULL);
05317    if (!genchannel)
05318    {
05319       fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
05320       ast_hangup(mychannel);
05321       pthread_exit(NULL);
05322    }
05323 #ifdef   AST_CDR_FLAG_POST_DISABLED
05324    if (genchannel->cdr)
05325       ast_set_flag(genchannel->cdr,AST_CDR_FLAG_POST_DISABLED);
05326 #endif
05327    ci.chan = 0;
05328    ci.confno = myrpt->conf;
05329    ci.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER
05330       | DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER; 
05331    /* first put the channel on the conference */
05332    if (ioctl(genchannel->fds[0],DAHDI_SETCONF,&ci) == -1)
05333    {
05334       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05335       ast_hangup(mychannel);
05336       ast_hangup(genchannel);
05337       myrpt->callmode = 0;
05338       pthread_exit(NULL);
05339    }
05340    if (myrpt->p.tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->p.tonezone) == -1))
05341    {
05342       ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.tonezone);
05343       ast_hangup(mychannel);
05344       ast_hangup(genchannel);
05345       myrpt->callmode = 0;
05346       pthread_exit(NULL);
05347    }
05348    if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->p.tonezone) == -1))
05349    {
05350       ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.tonezone);
05351       ast_hangup(mychannel);
05352       ast_hangup(genchannel);
05353       myrpt->callmode = 0;
05354       pthread_exit(NULL);
05355    }
05356    /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */
05357    if ((!myrpt->patchquiet) && (tone_zone_play_tone(genchannel->fds[0],DAHDI_TONE_DIALTONE) < 0))
05358    {
05359       ast_log(LOG_WARNING, "Cannot start dialtone\n");
05360       ast_hangup(mychannel);
05361       ast_hangup(genchannel);
05362       myrpt->callmode = 0;
05363       pthread_exit(NULL);
05364    }
05365    stopped = 0;
05366    congstarted = 0;
05367    dialtimer = 0;
05368    lastcidx = 0;
05369    myrpt->calldigittimer = 0;
05370    aborted = 0;
05371 
05372    while ((myrpt->callmode == 1) || (myrpt->callmode == 4))
05373    {
05374       if((myrpt->patchdialtime)&&(myrpt->callmode == 1)&&(myrpt->cidx != lastcidx)){
05375          dialtimer = 0;
05376          lastcidx = myrpt->cidx;
05377       }     
05378 
05379       if((myrpt->patchdialtime)&&(dialtimer >= myrpt->patchdialtime)){ 
05380           if(debug)
05381             ast_log(LOG_NOTICE, "dialtimer %i > patchdialtime %i\n", dialtimer,myrpt->patchdialtime);
05382          rpt_mutex_lock(&myrpt->lock);
05383          aborted = 1;
05384          myrpt->callmode = 0;
05385          rpt_mutex_unlock(&myrpt->lock);
05386          break;
05387       }
05388    
05389       if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0))
05390       {
05391          stopped = 1;
05392          /* stop dial tone */
05393          tone_zone_play_tone(genchannel->fds[0],-1);
05394       }
05395       if (myrpt->callmode == 1)
05396       {
05397          if(myrpt->calldigittimer > PATCH_DIALPLAN_TIMEOUT)
05398          {
05399             myrpt->callmode = 2;
05400             break;
05401          }
05402          /* bump timer if active */
05403          if (myrpt->calldigittimer) 
05404             myrpt->calldigittimer += MSWAIT;
05405       }
05406       if (myrpt->callmode == 4)
05407       {
05408          if(!congstarted){
05409             congstarted = 1;
05410             /* start congestion tone */
05411             tone_zone_play_tone(genchannel->fds[0],DAHDI_TONE_CONGESTION);
05412          }
05413       }
05414       res = ast_safe_sleep(mychannel, MSWAIT);
05415       if (res < 0)
05416       {
05417           if(debug)
05418             ast_log(LOG_NOTICE, "ast_safe_sleep=%i\n", res);
05419          ast_hangup(mychannel);
05420          ast_hangup(genchannel);
05421          rpt_mutex_lock(&myrpt->lock);
05422          myrpt->callmode = 0;
05423          rpt_mutex_unlock(&myrpt->lock);
05424          pthread_exit(NULL);
05425       }
05426       dialtimer += MSWAIT;
05427    }
05428    /* stop any tone generation */
05429    tone_zone_play_tone(genchannel->fds[0],-1);
05430    /* end if done */
05431    if (!myrpt->callmode)
05432    {
05433       if(debug)
05434          ast_log(LOG_NOTICE, "callmode==0\n");
05435       ast_hangup(mychannel);
05436       ast_hangup(genchannel);
05437       rpt_mutex_lock(&myrpt->lock);
05438       myrpt->callmode = 0;
05439       myrpt->macropatch=0;
05440       channel_revert(myrpt);
05441       rpt_mutex_unlock(&myrpt->lock);
05442       if((!myrpt->patchquiet) && aborted)
05443          rpt_telemetry(myrpt, TERM, NULL);
05444       pthread_exit(NULL);        
05445    }
05446 
05447    if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid){
05448       char *name, *loc, *instr;
05449       instr = ast_strdup(myrpt->p.ourcallerid);
05450       if(instr){
05451          ast_callerid_parse(instr, &name, &loc);
05452          if(loc){
05453             if(mychannel->cid.cid_num)
05454                ast_free(mychannel->cid.cid_num);
05455             mychannel->cid.cid_num = ast_strdup(loc);
05456          }
05457          if(name){
05458             if(mychannel->cid.cid_name)
05459                ast_free(mychannel->cid.cid_name);
05460             mychannel->cid.cid_name = ast_strdup(name);
05461          }
05462          ast_free(instr);
05463       }
05464    }
05465 
05466    ast_copy_string(mychannel->exten, myrpt->exten, sizeof(mychannel->exten) - 1);
05467    ast_copy_string(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context) - 1);
05468    
05469    if (myrpt->p.acctcode)
05470       ast_cdr_setaccount(mychannel,myrpt->p.acctcode);
05471    mychannel->priority = 1;
05472    ast_channel_undefer_dtmf(mychannel);
05473    if (ast_pbx_start(mychannel) < 0)
05474    {
05475       ast_log(LOG_WARNING, "Unable to start PBX!!\n");
05476       ast_hangup(mychannel);
05477       ast_hangup(genchannel);
05478       rpt_mutex_lock(&myrpt->lock);
05479       myrpt->callmode = 0;
05480       rpt_mutex_unlock(&myrpt->lock);
05481       pthread_exit(NULL);
05482    }
05483    usleep(10000);
05484    rpt_mutex_lock(&myrpt->lock);
05485    myrpt->callmode = 3;
05486    /* set appropriate conference for the pseudo */
05487    ci.chan = 0;
05488    ci.confno = myrpt->conf;
05489    ci.confmode = (myrpt->p.duplex == 2) ? DAHDI_CONF_CONFANNMON :
05490       (DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER);
05491    /* first put the channel on the conference in announce mode */
05492    if (ioctl(myrpt->pchannel->fds[0],DAHDI_SETCONF,&ci) == -1)
05493    {
05494       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05495       ast_hangup(mychannel);
05496       ast_hangup(genchannel);
05497       myrpt->callmode = 0;
05498       pthread_exit(NULL);
05499    }
05500    /* get its channel number */
05501    if (ioctl(mychannel->fds[0],DAHDI_CHANNO,&res) == -1)
05502    {
05503       ast_log(LOG_WARNING, "Unable to get autopatch channel number\n");
05504       ast_hangup(mychannel);
05505       myrpt->callmode = 0;
05506       pthread_exit(NULL);
05507    }
05508    ci.chan = 0;
05509    ci.confno = res;
05510    ci.confmode = DAHDI_CONF_MONITOR;
05511    /* put vox channel monitoring on the channel  */
05512    if (ioctl(myrpt->voxchannel->fds[0],DAHDI_SETCONF,&ci) == -1)
05513    {
05514       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05515       ast_hangup(mychannel);
05516       myrpt->callmode = 0;
05517       pthread_exit(NULL);
05518    }
05519    while(myrpt->callmode)
05520    {
05521       if ((!mychannel->pbx) && (myrpt->callmode != 4))
05522       {
05523           /* If patch is setup for far end disconnect */
05524          if(myrpt->patchfarenddisconnect || (myrpt->p.duplex < 2)){ 
05525             if(debug)ast_log(LOG_NOTICE,"callmode=%i, patchfarenddisconnect=%i, duplex=%i\n",\
05526                   myrpt->callmode,myrpt->patchfarenddisconnect,myrpt->p.duplex);
05527             myrpt->callmode = 0;
05528             myrpt->macropatch=0;
05529             if(!myrpt->patchquiet){
05530                rpt_mutex_unlock(&myrpt->lock);
05531                rpt_telemetry(myrpt, TERM, NULL);
05532                rpt_mutex_lock(&myrpt->lock);
05533             }
05534          }
05535          else{ /* Send congestion until patch is downed by command */
05536             myrpt->callmode = 4;
05537             rpt_mutex_unlock(&myrpt->lock);
05538             /* start congestion tone */
05539             tone_zone_play_tone(genchannel->fds[0],DAHDI_TONE_CONGESTION);
05540             rpt_mutex_lock(&myrpt->lock);
05541          }
05542       }
05543       if (myrpt->mydtmf)
05544       {
05545          struct ast_frame wf = {AST_FRAME_DTMF, } ;
05546          wf.subclass = myrpt->mydtmf;
05547          rpt_mutex_unlock(&myrpt->lock);
05548          ast_queue_frame(mychannel,&wf);
05549 #ifdef   NEW_ASTERISK
05550          ast_senddigit(genchannel,myrpt->mydtmf,0);
05551 #else
05552          ast_senddigit(genchannel,myrpt->mydtmf);
05553 #endif
05554          rpt_mutex_lock(&myrpt->lock);
05555          myrpt->mydtmf = 0;
05556       }
05557       rpt_mutex_unlock(&myrpt->lock);
05558       usleep(MSWAIT * 1000);
05559       rpt_mutex_lock(&myrpt->lock);
05560    }
05561    if(debug)
05562       ast_log(LOG_NOTICE, "exit channel loop\n");
05563    rpt_mutex_unlock(&myrpt->lock);
05564    tone_zone_play_tone(genchannel->fds[0],-1);
05565    if (mychannel->pbx) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV);
05566    ast_hangup(genchannel);
05567    rpt_mutex_lock(&myrpt->lock);
05568    myrpt->callmode = 0;
05569    myrpt->macropatch=0;
05570    channel_revert(myrpt);
05571    rpt_mutex_unlock(&myrpt->lock);
05572    /* set appropriate conference for the pseudo */
05573    ci.chan = 0;
05574    ci.confno = myrpt->conf;
05575    ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? DAHDI_CONF_CONFANNMON :
05576       (DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER);
05577    /* first put the channel on the conference in announce mode */
05578    if (ioctl(myrpt->pchannel->fds[0],DAHDI_SETCONF,&ci) == -1)
05579    {
05580       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05581    }
05582    pthread_exit(NULL);
05583 }
05584 
05585 static void send_link_dtmf(struct rpt *myrpt,char c)
05586 {
05587 char  str[300];
05588 struct   ast_frame wf;
05589 struct   rpt_link *l;
05590 
05591    snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c);
05592    wf.frametype = AST_FRAME_TEXT;
05593    wf.subclass = 0;
05594    wf.offset = 0;
05595    wf.mallocd = 0;
05596    wf.datalen = strlen(str) + 1;
05597    wf.samples = 0;
05598    l = myrpt->links.next;
05599    /* first, see if our dude is there */
05600    while(l != &myrpt->links)
05601    {
05602       if (l->name[0] == '0') 
05603       {
05604          l = l->next;
05605          continue;
05606       }
05607       /* if we found it, write it and were done */
05608       if (!strcmp(l->name,myrpt->cmdnode))
05609       {
05610          wf.data.ptr = str;
05611          if (l->chan) ast_write(l->chan,&wf);
05612          return;
05613       }
05614       l = l->next;
05615    }
05616    l = myrpt->links.next;
05617    /* if not, give it to everyone */
05618    while(l != &myrpt->links)
05619    {
05620       wf.data.ptr = str;
05621       if (l->chan) ast_write(l->chan,&wf);
05622       l = l->next;
05623    }
05624    return;
05625 }
05626 
05627 static void send_link_keyquery(struct rpt *myrpt)
05628 {
05629 char  str[300];
05630 struct   ast_frame wf;
05631 struct   rpt_link *l;
05632 
05633    rpt_mutex_lock(&myrpt->lock);
05634    memset(myrpt->topkey,0,sizeof(myrpt->topkey));
05635    myrpt->topkeystate = 1;
05636    time(&myrpt->topkeytime);
05637    rpt_mutex_unlock(&myrpt->lock);
05638    snprintf(str, sizeof(str), "K? * %s 0 0", myrpt->name);
05639    wf.frametype = AST_FRAME_TEXT;
05640    wf.subclass = 0;
05641    wf.offset = 0;
05642    wf.mallocd = 0;
05643    wf.datalen = strlen(str) + 1;
05644    wf.samples = 0;
05645    l = myrpt->links.next;
05646    /* give it to everyone */
05647    while(l != &myrpt->links)
05648    {
05649       wf.data.ptr = str;
05650       if (l->chan) ast_write(l->chan,&wf);
05651       l = l->next;
05652    }
05653    return;
05654 }
05655 
05656 /* send newkey request */
05657 
05658 static void send_newkey(struct ast_channel *chan)
05659 {
05660 
05661    /* ast_safe_sleep(chan,10); */
05662    ast_sendtext(chan,newkeystr);
05663    return;
05664 }
05665 
05666 
05667 /* 
05668  * Connect a link 
05669  *
05670  * Return values:
05671  * -2: Attempt to connect to self 
05672  * -1: No such node
05673  *  0: Success
05674  *  1: No match yet
05675  *  2: Already connected to this node
05676  */
05677 
05678 static int connect_link(struct rpt *myrpt, char* node, int mode, int perma)
05679 {
05680    char *val, *s, *s1, *s2, *tele;
05681    char lstr[MAXLINKLIST],*strs[MAXLINKLIST];
05682    char tmp[300], deststr[300] = "",modechange = 0;
05683    char sx[320],*sy;
05684    struct rpt_link *l;
05685    int reconnects = 0;
05686    int i,n;
05687    struct dahdi_confinfo ci;  /* conference info */
05688 
05689    val = node_lookup(myrpt,node);
05690    if (!val){
05691       if(strlen(node) >= myrpt->longestnode)
05692          return -1; /* No such node */
05693       return 1; /* No match yet */
05694    }
05695 
05696    if(!strcmp(myrpt->name,node)) /* Do not allow connections to self */
05697       return -2;
05698       
05699    if(debug > 3){
05700       ast_log(LOG_NOTICE,"Connect attempt to node %s\n", node);
05701       ast_log(LOG_NOTICE,"Mode: %s\n",(mode)?"Transceive":"Monitor");
05702       ast_log(LOG_NOTICE,"Connection type: %s\n",(perma)?"Permalink":"Normal");
05703    }
05704 
05705    strncpy(tmp,val,sizeof(tmp) - 1);
05706    s = tmp;
05707    s1 = strsep(&s,",");
05708    if (!strchr(s1,':') && strchr(s1,'/') && strncasecmp(s1, "local/", 6))
05709    {
05710       sy = strchr(s1,'/');    
05711       *sy = 0;
05712       sprintf(sx,"%s:4569/%s",s1,sy + 1);
05713       s1 = sx;
05714    }
05715    s2 = strsep(&s,",");
05716    rpt_mutex_lock(&myrpt->lock);
05717    l = myrpt->links.next;
05718    /* try to find this one in queue */
05719    while(l != &myrpt->links){
05720       if (l->name[0] == '0') 
05721       {
05722          l = l->next;
05723          continue;
05724       }
05725    /* if found matching string */
05726       if (!strcmp(l->name, node))
05727          break;
05728       l = l->next;
05729    }
05730    /* if found */
05731    if (l != &myrpt->links){ 
05732    /* if already in this mode, just ignore */
05733       if ((l->mode) || (!l->chan)) {
05734          rpt_mutex_unlock(&myrpt->lock);
05735          return 2; /* Already linked */
05736       }
05737       reconnects = l->reconnects;
05738       rpt_mutex_unlock(&myrpt->lock);
05739       if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
05740       l->retries = l->max_retries + 1;
05741       l->disced = 2;
05742       modechange = 1;
05743    } else
05744    {
05745       __mklinklist(myrpt,NULL,lstr);
05746       rpt_mutex_unlock(&myrpt->lock);
05747       n = finddelim(lstr,strs,MAXLINKLIST);
05748       for(i = 0; i < n; i++)
05749       {
05750          if ((*strs[i] < '0') || 
05751              (*strs[i] > '9')) strs[i]++;
05752          if (!strcmp(strs[i],node))
05753          {
05754             return 2; /* Already linked */
05755          }
05756       }
05757    }
05758    strncpy(myrpt->lastlinknode,node,MAXNODESTR - 1);
05759    /* establish call */
05760    l = ast_malloc(sizeof(struct rpt_link));
05761    if (!l)
05762    {
05763       ast_log(LOG_WARNING, "Unable to malloc\n");
05764       return -1;
05765    }
05766    /* zero the silly thing */
05767    memset((char *)l,0,sizeof(struct rpt_link));
05768    l->mode = mode;
05769    l->outbound = 1;
05770    l->thisconnected = 0;
05771    voxinit_link(l,1);
05772    strncpy(l->name, node, MAXNODESTR - 1);
05773    l->isremote = (s && ast_true(s));
05774    if (modechange) l->connected = 1;
05775    l->hasconnected = l->perma = perma;
05776 #ifdef ALLOW_LOCAL_CHANNELS
05777    if ((strncasecmp(s1,"iax2/", 5) == 0) || (strncasecmp(s1, "local/", 6) == 0))
05778          strncpy(deststr, s1, sizeof(deststr));
05779    else
05780            snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
05781 #else
05782    snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
05783 #endif
05784    tele = strchr(deststr, '/');
05785    if (!tele){
05786       ast_log(LOG_WARNING,"link3:Dial number (%s) must be in format tech/number\n",deststr);
05787       ast_free(l);
05788       return -1;
05789    }
05790    *tele++ = 0;
05791    l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL);
05792    if (l->chan){
05793       ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
05794       ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
05795 #ifdef   AST_CDR_FLAG_POST_DISABLED
05796       if (l->chan->cdr)
05797          ast_set_flag(l->chan->cdr,AST_CDR_FLAG_POST_DISABLED);
05798 #endif
05799 #ifndef  NEW_ASTERISK
05800       l->chan->whentohangup = 0;
05801 #endif
05802       l->chan->appl = "Apprpt";
05803       l->chan->data = "(Remote Rx)";
05804       if (debug > 3)
05805          ast_log(LOG_NOTICE, "rpt (remote) initiating call to %s/%s on %s\n",
05806       deststr, tele, l->chan->name);
05807       if(l->chan->cid.cid_num)
05808          ast_free(l->chan->cid.cid_num);
05809       l->chan->cid.cid_num = ast_strdup(myrpt->name);
05810       ast_call(l->chan,tele,999);
05811    }
05812    else {
05813       if(debug > 3) 
05814          ast_log(LOG_NOTICE, "Unable to place call to %s/%s on %s\n",
05815       deststr,tele,l->chan->name);
05816       if (myrpt->p.archivedir)
05817       {
05818          char str[100];
05819          sprintf(str,"LINKFAIL,%s",l->name);
05820          donodelog(myrpt,str);
05821       }
05822       ast_free(l);
05823       return -1;
05824    }
05825    /* allocate a pseudo-channel thru asterisk */
05826    l->pchan = ast_request("DAHDI",AST_FORMAT_SLINEAR,"pseudo",NULL);
05827    if (!l->pchan){
05828       ast_log(LOG_WARNING,"rpt connect: Sorry unable to obtain pseudo channel\n");
05829       ast_hangup(l->chan);
05830       ast_free(l);
05831       return -1;
05832    }
05833    ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
05834    ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
05835 #ifdef   AST_CDR_FLAG_POST_DISABLED
05836    if (l->pchan->cdr)
05837       ast_set_flag(l->pchan->cdr,AST_CDR_FLAG_POST_DISABLED);
05838 #endif
05839    /* make a conference for the tx */
05840    ci.chan = 0;
05841    ci.confno = myrpt->conf;
05842    ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER;
05843    /* first put the channel on the conference in proper mode */
05844    if (ioctl(l->pchan->fds[0], DAHDI_SETCONF, &ci) == -1)
05845    {
05846       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
05847       ast_hangup(l->chan);
05848       ast_hangup(l->pchan);
05849       ast_free(l);
05850       return -1;
05851    }
05852    rpt_mutex_lock(&myrpt->lock);
05853    l->reconnects = reconnects;
05854    /* insert at end of queue */
05855    l->max_retries = MAX_RETRIES;
05856    if (perma)
05857       l->max_retries = MAX_RETRIES_PERM;
05858    if (l->isremote) l->retries = l->max_retries + 1;
05859    insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
05860    __kickshort(myrpt);
05861    rpt_mutex_unlock(&myrpt->lock);
05862    if (!l->phonemode) send_newkey(l->chan);
05863    return 0;
05864 }
05865 
05866 
05867 
05868 /*
05869 * Internet linking function 
05870 */
05871 
05872 static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink)
05873 {
05874 
05875    char *val, *s, *s1, *s2;
05876    char tmp[300];
05877    char digitbuf[MAXNODESTR],*strs[MAXLINKLIST];
05878    char mode,perma;
05879    char sx[320],*sy;
05880    struct rpt_link *l;
05881    int i,r;
05882 
05883    if(!param)
05884       return DC_ERROR;
05885       
05886          
05887    if (myrpt->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable )
05888       return DC_ERROR;
05889 
05890    strncpy(digitbuf,digits,MAXNODESTR - 1);
05891 
05892    if(debug > 6)
05893       printf("@@@@ ilink param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
05894       
05895    switch(myatoi(param)){
05896       case 11: /* Perm Link off */
05897       case 1: /* Link off */
05898          if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
05899             strcpy(digitbuf,myrpt->lastlinknode);
05900          val = node_lookup(myrpt,digitbuf);
05901          if (!val){
05902             if(strlen(digitbuf) >= myrpt->longestnode)
05903                return DC_ERROR;
05904             break;
05905          }
05906          strncpy(tmp,val,sizeof(tmp) - 1);
05907          s = tmp;
05908          s1 = strsep(&s,",");
05909          if (!strchr(s1,':') && strchr(s1,'/') && strncasecmp(s1, "local/", 6))
05910          {
05911             sy = strchr(s1,'/');    
05912             *sy = 0;
05913             sprintf(sx,"%s:4569/%s",s1,sy + 1);
05914             s1 = sx;
05915          }
05916          s2 = strsep(&s,",");
05917          rpt_mutex_lock(&myrpt->lock);
05918          l = myrpt->links.next;
05919          /* try to find this one in queue */
05920          while(l != &myrpt->links){
05921             if (l->name[0] == '0') 
05922             {
05923                l = l->next;
05924                continue;
05925             }
05926             /* if found matching string */
05927             if (!strcmp(l->name, digitbuf))
05928                break;
05929             l = l->next;
05930          }
05931          if (l != &myrpt->links){ /* if found */
05932             struct   ast_frame wf;
05933 
05934             /* must use perm command on perm link */
05935             if ((myatoi(param) < 10) && 
05936                 (l->max_retries > MAX_RETRIES))
05937             {
05938                rpt_mutex_unlock(&myrpt->lock);
05939                return DC_COMPLETE;
05940             }
05941             strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1);
05942             l->retries = l->max_retries + 1;
05943             l->disced = 1;
05944             rpt_mutex_unlock(&myrpt->lock);
05945             wf.frametype = AST_FRAME_TEXT;
05946             wf.subclass = 0;
05947             wf.offset = 0;
05948             wf.mallocd = 0;
05949             wf.datalen = strlen(discstr) + 1;
05950             wf.samples = 0;
05951             wf.data.ptr = discstr;
05952             if (l->chan)
05953             {
05954                ast_write(l->chan,&wf);
05955                if (ast_safe_sleep(l->chan,250) == -1) return DC_ERROR;
05956                ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
05957             }
05958             rpt_telemetry(myrpt, COMPLETE, NULL);
05959             return DC_COMPLETE;
05960          }
05961          rpt_mutex_unlock(&myrpt->lock);  
05962          return DC_COMPLETE;
05963       case 2: /* Link Monitor */
05964       case 3: /* Link transceive */
05965       case 12: /* Link Monitor permanent */
05966       case 13: /* Link transceive permanent */
05967          if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
05968             strcpy(digitbuf,myrpt->lastlinknode);
05969          /* Attempt connection  */
05970          perma = (atoi(param) > 10) ? 1 : 0;
05971          mode = (atoi(param) & 1) ? 1 : 0;
05972          r = connect_link(myrpt, digitbuf, mode, perma);
05973          switch(r){
05974             case -2: /* Attempt to connect to self */
05975                return DC_COMPLETE; /* Silent error */
05976 
05977             case 0:
05978                rpt_telemetry(myrpt, COMPLETE, NULL);
05979                return DC_COMPLETE;
05980 
05981             case 1:
05982                break;
05983             
05984             case 2:
05985                rpt_telemetry(myrpt, REMALREADY, NULL);
05986                return DC_COMPLETE;
05987             
05988             default:
05989                rpt_telemetry(myrpt, CONNFAIL, NULL);
05990                return DC_COMPLETE;
05991          }
05992          break;
05993 
05994       case 4: /* Enter Command Mode */
05995       
05996          /* if doesnt allow link cmd, or no links active, return */
05997          if (((command_source != SOURCE_RPT) && 
05998             (command_source != SOURCE_PHONE) &&
05999             (command_source != SOURCE_ALT) &&
06000             (command_source != SOURCE_DPHONE)) ||
06001              (myrpt->links.next == &myrpt->links))
06002             return DC_COMPLETE;
06003          
06004          /* if already in cmd mode, or selected self, fughetabahtit */
06005          if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))){
06006          
06007             rpt_telemetry(myrpt, REMALREADY, NULL);
06008             return DC_COMPLETE;
06009          }
06010          if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
06011             strcpy(digitbuf,myrpt->lastlinknode);
06012          /* node must at least exist in list */
06013          val = node_lookup(myrpt,digitbuf);
06014          if (!val){
06015             if(strlen(digitbuf) >= myrpt->longestnode)
06016                return DC_ERROR;
06017             break;
06018          
06019          }
06020          rpt_mutex_lock(&myrpt->lock);
06021          strcpy(myrpt->lastlinknode,digitbuf);
06022          strncpy(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode) - 1);
06023          rpt_mutex_unlock(&myrpt->lock);
06024          rpt_telemetry(myrpt, REMGO, NULL);  
06025          return DC_COMPLETE;
06026          
06027       case 5: /* Status */
06028          rpt_telemetry(myrpt, STATUS, NULL);
06029          return DC_COMPLETE;
06030 
06031       case 15: /* Full Status */
06032          rpt_telemetry(myrpt, FULLSTATUS, NULL);
06033          return DC_COMPLETE;
06034          
06035          
06036       case 6: /* All Links Off, including permalinks */
06037                        rpt_mutex_lock(&myrpt->lock);
06038          myrpt->savednodes[0] = 0;
06039                         l = myrpt->links.next;
06040                         /* loop through all links */
06041                         while(l != &myrpt->links){
06042             struct   ast_frame wf;
06043                                 if (l->name[0] == '0') /* Skip any IAXRPT monitoring */
06044                                 {
06045                                         l = l->next;
06046                                         continue;
06047                                 }
06048             /* Make a string of disconnected nodes for possible restoration */
06049             sprintf(tmp,"%c%c%s",(l->mode) ? 'X' : 'M',(l->perma) ? 'P':'T',l->name);
06050             if(strlen(tmp) + strlen(myrpt->savednodes) + 1 < MAXNODESTR){ 
06051                if(myrpt->savednodes[0])
06052                   strcat(myrpt->savednodes, ",");
06053                strcat(myrpt->savednodes, tmp);
06054             }
06055                               l->retries = l->max_retries + 1;
06056                                 l->disced = 2; /* Silently disconnect */
06057                                 rpt_mutex_unlock(&myrpt->lock);
06058             /* ast_log(LOG_NOTICE,"dumping link %s\n",l->name); */
06059                                 
06060                                 wf.frametype = AST_FRAME_TEXT;
06061                                 wf.subclass = 0;
06062                                 wf.offset = 0;
06063                                 wf.mallocd = 0;
06064                                 wf.datalen = strlen(discstr) + 1;
06065                                 wf.samples = 0;
06066                                 wf.data.ptr = discstr;
06067                                 if (l->chan)
06068                                 {
06069                                         ast_write(l->chan,&wf);
06070                                         ast_safe_sleep(l->chan,250); /* It's dead already, why check the return value? */
06071                                         ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
06072                                 }
06073             rpt_mutex_lock(&myrpt->lock);
06074                                 l = l->next;
06075                         }
06076          rpt_mutex_unlock(&myrpt->lock);
06077          if(debug > 3)
06078             ast_log(LOG_NOTICE,"Nodes disconnected: %s\n",myrpt->savednodes);
06079                         rpt_telemetry(myrpt, COMPLETE, NULL);
06080          return DC_COMPLETE;
06081 
06082       case 7: /* Identify last node which keyed us up */
06083          rpt_telemetry(myrpt, LASTNODEKEY, NULL);
06084          break;
06085 
06086 
06087 #ifdef   _MDC_DECODE_H_
06088       case 8:
06089          myrpt->lastunit = 0xd00d; 
06090          mdc1200_notify(myrpt,NULL,myrpt->lastunit);
06091          mdc1200_send(myrpt,myrpt->lastunit);
06092          break;
06093 #endif
06094 
06095       case 16: /* Restore links disconnected with "disconnect all links" command */
06096          strcpy(tmp, myrpt->savednodes); /* Make a copy */
06097          finddelim(tmp, strs, MAXLINKLIST); /* convert into substrings */
06098          for(i = 0; tmp[0] && strs[i] != NULL && i < MAXLINKLIST; i++){
06099             s1 = strs[i];
06100             mode = (s1[0] == 'X') ? 1 : 0;
06101             perma = (s1[1] == 'P') ? 1 : 0;
06102             connect_link(myrpt, s1 + 2, mode, perma); /* Try to reconnect */
06103          }
06104                         rpt_telemetry(myrpt, COMPLETE, NULL);
06105          break;
06106    
06107       case 200:
06108       case 201:
06109       case 202:
06110       case 203:
06111       case 204:
06112       case 205:
06113       case 206:
06114       case 207:
06115       case 208:
06116       case 209:
06117       case 210:
06118       case 211:
06119       case 212:
06120       case 213:
06121       case 214:
06122       case 215:
06123          if (((myrpt->p.propagate_dtmf) && 
06124               (command_source == SOURCE_LNK)) ||
06125              ((myrpt->p.propagate_phonedtmf) &&
06126             ((command_source == SOURCE_PHONE) ||
06127               (command_source == SOURCE_ALT) ||
06128                 (command_source == SOURCE_DPHONE))))
06129                do_dtmf_local(myrpt,
06130                   remdtmfstr[myatoi(param) - 200]);
06131       default:
06132          return DC_ERROR;
06133          
06134    }
06135    
06136    return DC_INDETERMINATE;
06137 }  
06138 
06139 /*
06140 * Autopatch up
06141 */
06142 
06143 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06144 {
06145    pthread_attr_t attr;
06146    int i, idx, paramlength;
06147    char *lparam;
06148    char *value = NULL;
06149    char *paramlist[20];
06150 
06151    static char *keywords[] = {
06152    "context",
06153    "dialtime",
06154    "farenddisconnect",
06155    "noct",
06156    "quiet",
06157    NULL
06158    };
06159       
06160    if (myrpt->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable)
06161       return DC_ERROR;
06162       
06163    if(debug)
06164       printf("@@@@ Autopatch up\n");
06165 
06166    if(!myrpt->callmode){
06167       /* Set defaults */
06168       myrpt->patchnoct = 0;
06169       myrpt->patchdialtime = 0;
06170       myrpt->patchfarenddisconnect = 0;
06171       myrpt->patchquiet = 0;
06172       strncpy(myrpt->patchcontext, myrpt->p.ourcontext, MAXPATCHCONTEXT);
06173 
06174       if(param){
06175          /* Process parameter list */
06176          lparam = ast_strdup(param);
06177          if(!lparam){
06178             ast_log(LOG_ERROR,"App_rpt out of memory on line %d\n",__LINE__);
06179             return DC_ERROR;  
06180          }
06181          paramlength = finddelim(lparam, paramlist, 20);          
06182          for(i = 0; i < paramlength; i++){
06183             idx = matchkeyword(paramlist[i], &value, keywords);
06184             if(value)
06185                value = skipchars(value, "= ");
06186             switch(idx){
06187 
06188                case 1: /* context */
06189                   strncpy(myrpt->patchcontext, value, MAXPATCHCONTEXT - 1) ;
06190                   break;
06191                   
06192                case 2: /* dialtime */
06193                   myrpt->patchdialtime = atoi(value);
06194                   break;
06195 
06196                case 3: /* farenddisconnect */
06197                   myrpt->patchfarenddisconnect = atoi(value);
06198                   break;
06199 
06200                case 4:  /* noct */
06201                   myrpt->patchnoct = atoi(value);
06202                   break;
06203 
06204                case 5: /* quiet */
06205                   myrpt->patchquiet = atoi(value);
06206                   break;
06207                            
06208                default:
06209                   break;
06210             }
06211          }
06212       ast_free(lparam);
06213       }
06214    }
06215                
06216    rpt_mutex_lock(&myrpt->lock);
06217 
06218    /* if on call, force * into current audio stream */
06219    
06220    if ((myrpt->callmode == 2) || (myrpt->callmode == 3)){
06221       myrpt->mydtmf = myrpt->p.endchar;
06222    }
06223    if (myrpt->callmode){
06224       rpt_mutex_unlock(&myrpt->lock);
06225       return DC_COMPLETE;
06226    }
06227    myrpt->callmode = 1;
06228    myrpt->cidx = 0;
06229    myrpt->exten[myrpt->cidx] = 0;
06230    rpt_mutex_unlock(&myrpt->lock);
06231    pthread_attr_init(&attr);
06232    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
06233    ast_pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *) myrpt);
06234    return DC_COMPLETE;
06235 }
06236 
06237 /*
06238 * Autopatch down
06239 */
06240 
06241 static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06242 {
06243    if (myrpt->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable)
06244       return DC_ERROR;
06245    
06246    if(debug)
06247       printf("@@@@ Autopatch down\n");
06248       
06249    rpt_mutex_lock(&myrpt->lock);
06250    
06251    myrpt->macropatch=0;
06252 
06253    if (!myrpt->callmode){
06254       rpt_mutex_unlock(&myrpt->lock);
06255       return DC_COMPLETE;
06256    }
06257    
06258    myrpt->callmode = 0;
06259    channel_revert(myrpt);
06260    rpt_mutex_unlock(&myrpt->lock);
06261    rpt_telemetry(myrpt, TERM, NULL);
06262    return DC_COMPLETE;
06263 }
06264 
06265 /*
06266 * Status
06267 */
06268 
06269 static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06270 {
06271 
06272    if (!param)
06273       return DC_ERROR;
06274 
06275    if ((myrpt->p.s[myrpt->p.sysstate_cur].txdisable) || (myrpt->p.s[myrpt->p.sysstate_cur].userfundisable))
06276       return DC_ERROR;
06277 
06278    if(debug)
06279       printf("@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
06280    
06281    switch(myatoi(param)){
06282       case 1: /* System ID */
06283          rpt_telemetry(myrpt, ID1, NULL);
06284          return DC_COMPLETE;
06285       case 2: /* System Time */
06286          rpt_telemetry(myrpt, STATS_TIME, NULL);
06287          return DC_COMPLETE;
06288       case 3: /* app_rpt.c version */
06289          rpt_telemetry(myrpt, STATS_VERSION, NULL);
06290          return DC_COMPLETE;
06291       case 11: /* System ID (local only)*/
06292           rpt_telemetry(myrpt, ID , NULL);
06293             return DC_COMPLETE;
06294         case 12: /* System Time (local only)*/
06295             rpt_telemetry(myrpt, STATS_TIME_LOCAL, NULL);
06296             return DC_COMPLETE;
06297       default:
06298          return DC_ERROR;
06299    }
06300    return DC_INDETERMINATE;
06301 }
06302 /*
06303 *  Macro-oni (without Salami)
06304 */
06305 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06306 {
06307 char  *val;
06308 int   i;
06309    if (myrpt->remote)
06310       return DC_ERROR;
06311 
06312    if(debug) 
06313       printf("@@@@ macro-oni param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
06314    
06315    if(strlen(digitbuf) < 1) /* needs 1 digit */
06316       return DC_INDETERMINATE;
06317          
06318    for(i = 0 ; i < digitbuf[i] ; i++) {
06319       if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
06320          return DC_ERROR;
06321    }
06322    
06323    if (*digitbuf == '0') val = myrpt->p.startupmacro;
06324    else val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf);
06325    /* param was 1 for local buf */
06326    if (!val){
06327                 if (strlen(digitbuf) < myrpt->macro_longest)
06328                         return DC_INDETERMINATE;
06329       rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
06330       return DC_COMPLETE;
06331    }        
06332    rpt_mutex_lock(&myrpt->lock);
06333    if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val))
06334    {
06335       rpt_mutex_unlock(&myrpt->lock);
06336       rpt_telemetry(myrpt, MACRO_BUSY, NULL);
06337       return DC_ERROR;
06338    }
06339    myrpt->macrotimer = MACROTIME;
06340    strncat(myrpt->macrobuf,val,MAXMACRO - 1);
06341    rpt_mutex_unlock(&myrpt->lock);
06342    return DC_COMPLETE;  
06343 }
06344 
06345 /*
06346 *  Playback a recording
06347 */
06348 
06349 static int function_playback(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06350 {
06351 
06352    if (myrpt->remote)
06353       return DC_ERROR;
06354 
06355    if(debug) 
06356       printf("@@@@ playback param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
06357    
06358    if (ast_fileexists(param,NULL,myrpt->rxchannel->language) <= 0)
06359       return DC_ERROR;
06360 
06361    rpt_telemetry(myrpt,PLAYBACK,param);
06362    return DC_COMPLETE;
06363 }
06364 
06365 /*
06366 * COP - Control operator
06367 */
06368 
06369 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
06370 {
06371    char string[16];
06372    int res;
06373 
06374    int i, r;
06375 
06376    if(!param)
06377       return DC_ERROR;
06378    
06379    switch(myatoi(param)){
06380       case 1: /* System reset */
06381          res = system("killall -9 asterisk");
06382          return DC_COMPLETE;
06383 
06384       case 2:
06385          myrpt->p.s[myrpt->p.sysstate_cur].txdisable = 0;
06386          rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA");
06387          return DC_COMPLETE;
06388          
06389       case 3:
06390          myrpt->p.s[myrpt->p.sysstate_cur].txdisable = 1;
06391          return DC_COMPLETE;
06392          
06393       case 4: /* test tone on */
06394          if (myrpt->stopgen < 0) 
06395          {
06396             myrpt->stopgen = 1;
06397          }
06398          else 
06399          {
06400             myrpt->stopgen = 0;
06401             rpt_telemetry(myrpt, TEST_TONE, NULL);
06402          }
06403          return DC_COMPLETE;
06404 
06405       case 5: /* Disgorge variables to log for debug purposes */
06406          myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
06407          return DC_COMPLETE;
06408 
06409       case 6: /* Simulate COR being activated (phone only) */
06410          if (command_source != SOURCE_PHONE) return DC_INDETERMINATE;
06411          return DC_DOKEY;  
06412 
06413 
06414       case 7: /* Time out timer enable */
06415          myrpt->p.s[myrpt->p.sysstate_cur].totdisable = 0;
06416          rpt_telemetry(myrpt, ARB_ALPHA, (void *) "TOTENA");
06417          return DC_COMPLETE;
06418          
06419       case 8: /* Time out timer disable */
06420          myrpt->p.s[myrpt->p.sysstate_cur].totdisable = 1;
06421          rpt_telemetry(myrpt, ARB_ALPHA, (void *) "TOTDIS");
06422          return DC_COMPLETE;
06423 
06424                 case 9: /* Autopatch enable */
06425                         myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable = 0;
06426                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "APENA");
06427                         return DC_COMPLETE;
06428 
06429                 case 10: /* Autopatch disable */
06430                         myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable = 1;
06431                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "APDIS");
06432                         return DC_COMPLETE;
06433 
06434                 case 11: /* Link Enable */
06435                         myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable = 0;
06436                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "LNKENA");
06437                         return DC_COMPLETE;
06438 
06439                 case 12: /* Link Disable */
06440                         myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable = 1;
06441                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "LNKDIS");
06442                         return DC_COMPLETE;
06443 
06444       case 13: /* Query System State */
06445          string[0] = string[1] = 'S';
06446          string[2] = myrpt->p.sysstate_cur + '0';
06447          string[3] = '\0';
06448          rpt_telemetry(myrpt, ARB_ALPHA, (void *) string);
06449          return DC_COMPLETE;
06450 
06451       case 14: /* Change System State */
06452          if(strlen(digitbuf) == 0)
06453             break;
06454          if((digitbuf[0] < '0') || (digitbuf[0] > '9'))
06455             return DC_ERROR;
06456          myrpt->p.sysstate_cur = digitbuf[0] - '0';
06457                         string[0] = string[1] = 'S';
06458                         string[2] = myrpt->p.sysstate_cur + '0';
06459                         string[3] = '\0';
06460                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) string);
06461                         return DC_COMPLETE;
06462 
06463                 case 15: /* Scheduler Enable */
06464                         myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable = 0;
06465                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "SKENA");
06466                         return DC_COMPLETE;
06467 
06468                 case 16: /* Scheduler Disable */
06469                         myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable = 1;
06470                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "SKDIS");
06471                         return DC_COMPLETE;
06472 
06473                 case 17: /* User functions Enable */
06474                         myrpt->p.s[myrpt->p.sysstate_cur].userfundisable = 0;
06475                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "UFENA");
06476                         return DC_COMPLETE;
06477 
06478                 case 18: /* User Functions Disable */
06479                         myrpt->p.s[myrpt->p.sysstate_cur].userfundisable = 1;
06480                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "UFDIS");
06481                         return DC_COMPLETE;
06482 
06483                 case 19: /* Alternate Tail Enable */
06484                         myrpt->p.s[myrpt->p.sysstate_cur].alternatetail = 1;
06485                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "ATENA");
06486                         return DC_COMPLETE;
06487 
06488                 case 20: /* Alternate Tail Disable */
06489                         myrpt->p.s[myrpt->p.sysstate_cur].alternatetail = 0;
06490                         rpt_telemetry(myrpt, ARB_ALPHA, (void *) "ATDIS");
06491                         return DC_COMPLETE;
06492 
06493                 case 21: /* Parrot Mode Disable */
06494          birdbath(myrpt);
06495          if (myrpt->p.parrotmode < 2)
06496          {
06497             myrpt->p.parrotmode = 0;
06498             rpt_telemetry(myrpt,COMPLETE,NULL);
06499             return DC_COMPLETE;
06500          }
06501          break;
06502 
06503                 case 22: /* Parrot Mode Enable */
06504          birdbath(myrpt);
06505          if (myrpt->p.parrotmode < 2)
06506          {
06507             myrpt->p.parrotmode = 1;
06508             rpt_telemetry(myrpt,COMPLETE,NULL);
06509             return DC_COMPLETE;
06510          }
06511          break;
06512       case 23: /* flush parrot in progress */
06513          birdbath(myrpt);
06514          rpt_telemetry(myrpt,COMPLETE,NULL);
06515          return DC_COMPLETE;
06516       case 24: /* flush all telemetry */
06517          flush_telem(myrpt);
06518          rpt_telemetry(myrpt,COMPLETE,NULL);
06519          return DC_COMPLETE;
06520       case 25: /* request keying info (brief) */
06521          send_link_keyquery(myrpt);
06522          myrpt->topkeylong = 0;
06523          rpt_telemetry(myrpt,COMPLETE,NULL);
06524          return DC_COMPLETE;
06525       case 26: /* request keying info (full) */
06526          send_link_keyquery(myrpt);
06527          myrpt->topkeylong = 1;
06528          rpt_telemetry(myrpt,COMPLETE,NULL);
06529          return DC_COMPLETE;
06530 
06531       case 30: /* recall memory location on programmable radio */
06532 
06533          if(strlen(digitbuf) < 2) /* needs 2 digits */
06534             break;
06535          
06536          for(i = 0 ; i < 2 ; i++){
06537             if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
06538                return DC_ERROR;
06539          }
06540        
06541          r = retreive_memory(myrpt, digitbuf);
06542          if (r < 0){
06543             rpt_telemetry(myrpt,MEMNOTFOUND,NULL);
06544             return DC_COMPLETE;
06545          }
06546          if (r > 0){
06547             return DC_ERROR;
06548          }
06549          if (setrem(myrpt) == -1) return DC_ERROR;
06550          return DC_COMPLETE;  
06551 
06552       case 31: 
06553           /* set channel. note that it's going to change channel 
06554              then confirm on the new channel! */
06555          if(strlen(digitbuf) < 2) /* needs 2 digits */
06556             break;
06557          
06558          for(i = 0 ; i < 2 ; i++){
06559             if((digitbuf[i] < '0') || (digitbuf[i] > '9'))
06560                return DC_ERROR;
06561          }
06562          channel_steer(myrpt,digitbuf);
06563          return DC_COMPLETE;  
06564 
06565       case 32: /* Touch Tone Pad Test */
06566          i = strlen(digitbuf);
06567          if(!i){
06568             if(debug > 3)
06569             ast_log(LOG_NOTICE,"Padtest entered");
06570             myrpt->inpadtest = 1;
06571          }
06572          else{
06573             if(debug > 3)
06574                ast_log(LOG_NOTICE,"Padtest len= %d digits=%s",i,digitbuf);
06575             if(digitbuf[i-1] != myrpt->p.endchar)
06576                break;
06577             rpt_telemetry(myrpt, ARB_ALPHA, digitbuf);
06578             myrpt->inpadtest = 0;
06579             if(debug > 3)
06580                ast_log(LOG_NOTICE,"Padtest exited");
06581             return DC_COMPLETE;
06582          }
06583    }  
06584    return DC_INDETERMINATE;
06585 }
06586 /*
06587 * Collect digits one by one until something matches
06588 */
06589 static int collect_function_digits(struct rpt *myrpt, char *digits, 
06590    int command_source, struct rpt_link *mylink)
06591 {
06592    int i,rv;
06593    char *stringp,*action,*param,*functiondigits;
06594    char function_table_name[30] = "";
06595    char workstring[200];
06596    
06597    struct ast_variable *vp;
06598    
06599    if (debug > 6) ast_log(LOG_NOTICE,"digits=%s  source=%d\n",digits, command_source);
06600 
06601    //if(debug) 
06602    // printf("@@@@ Digits collected: %s, source: %d\n", digits, command_source);
06603    
06604    if (command_source == SOURCE_DPHONE) {
06605       if (!myrpt->p.dphone_functions) return DC_INDETERMINATE;
06606       strncpy(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name) - 1);
06607       }
06608    else if (command_source == SOURCE_ALT) {
06609       if (!myrpt->p.alt_functions) return DC_INDETERMINATE;
06610       strncpy(function_table_name, myrpt->p.alt_functions, sizeof(function_table_name) - 1);
06611       }
06612    else if (command_source == SOURCE_PHONE) {
06613       if (!myrpt->p.phone_functions) return DC_INDETERMINATE;
06614       strncpy(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name) - 1);
06615       }
06616    else if (command_source == SOURCE_LNK)
06617       strncpy(function_table_name, myrpt->p.link_functions, sizeof(function_table_name) - 1);
06618    else
06619       strncpy(function_table_name, myrpt->p.functions, sizeof(function_table_name) - 1);
06620     /* find context for function table in rpt.conf file */
06621    vp = ast_variable_browse(myrpt->cfg, function_table_name);
06622    while(vp) {
06623       if(!strncasecmp(vp->name, digits, strlen(vp->name)))
06624          break;
06625       vp = vp->next;
06626    }  
06627    /* if function context not found */
06628    if(!vp) {
06629       int n;
06630 
06631       n = myrpt->longestfunc;
06632       if (command_source == SOURCE_LNK) n = myrpt->link_longestfunc;
06633       else 
06634       if (command_source == SOURCE_PHONE) n = myrpt->phone_longestfunc;
06635       else 
06636       if (command_source == SOURCE_ALT) n = myrpt->alt_longestfunc;
06637       else 
06638       if (command_source == SOURCE_DPHONE) n = myrpt->dphone_longestfunc;
06639       
06640       if(strlen(digits) >= n)
06641          return DC_ERROR;
06642       else
06643          return DC_INDETERMINATE;
06644    }  
06645    /* Found a match, retrieve value part and parse */
06646    strncpy(workstring, vp->value, sizeof(workstring) - 1 );
06647    stringp = workstring;
06648    action = strsep(&stringp, ",");
06649    param = stringp;
06650    if(debug)
06651       printf("@@@@ action: %s, param = %s\n",action, (param) ? param : "(null)");
06652    /* Look up the action */
06653    for(i = 0 ; i < (sizeof(function_table)/sizeof(struct function_table_tag)); i++){
06654       if(!strncasecmp(action, function_table[i].action, strlen(action)))
06655          break;
06656    }
06657    if(debug)
06658       printf("@@@@ table index i = %d\n",i);
06659    if(i == (sizeof(function_table)/sizeof(struct function_table_tag))){
06660       /* Error, action not in table */
06661       return DC_ERROR;
06662    }
06663    if(function_table[i].function == NULL){
06664       /* Error, function undefined */
06665       if(debug)
06666          printf("@@@@ NULL for action: %s\n",action);
06667       return DC_ERROR;
06668    }
06669    functiondigits = digits + strlen(vp->name);
06670    rv=(*function_table[i].function)(myrpt, param, functiondigits, command_source, mylink);
06671    if (debug > 6) ast_log(LOG_NOTICE,"rv=%i\n",rv);
06672    return(rv);
06673 }
06674 
06675 
06676 static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink,
06677    char *str)
06678 {
06679 /* XXX ATTENTION: if you change the size of these arrays you MUST
06680  * change the limits in corresponding sscanf() calls below. */
06681 char  tmp[512],tmp1[512],cmd[300] = "",dest[300],src[300],c;
06682 int   i,seq, res, ts;
06683 struct rpt_link *l;
06684 struct   ast_frame wf;
06685 
06686    wf.frametype = AST_FRAME_TEXT;
06687    wf.subclass = 0;
06688    wf.offset = 0;
06689    wf.mallocd = 0;
06690    wf.datalen = strlen(str) + 1;
06691    wf.samples = 0;
06692    /* put string in our buffer */
06693    strncpy(tmp,str,sizeof(tmp) - 1);
06694 
06695         if (!strcmp(tmp,discstr))
06696         {
06697                 mylink->disced = 1;
06698       mylink->retries = mylink->max_retries + 1;
06699                 ast_softhangup(mylink->chan,AST_SOFTHANGUP_DEV);
06700                 return;
06701         }
06702         if (!strcmp(tmp,newkeystr))
06703         {
06704       mylink->newkey = 1;
06705                 return;
06706         }
06707    if (tmp[0] == 'L')
06708    {
06709       rpt_mutex_lock(&myrpt->lock);
06710       strcpy(mylink->linklist,tmp + 2);
06711       time(&mylink->linklistreceived);
06712       rpt_mutex_unlock(&myrpt->lock);
06713       if (debug > 6) ast_log(LOG_NOTICE,"@@@@ node %s received node list %s from node %s\n",
06714          myrpt->name,tmp,mylink->name);
06715       return;
06716    }
06717    if (tmp[0] == 'K')
06718    {
06719       if (sscanf(tmp, "%299s %299s %299s %30d %30d", cmd, dest, src, &seq, &ts) != 5)
06720       {
06721          ast_log(LOG_WARNING, "Unable to parse keying string %s\n",str);
06722          return;
06723       }
06724       if (dest[0] == '0')
06725       {
06726          strcpy(dest,myrpt->name);
06727       }     
06728       /* if not for me, redistribute to all links */
06729       if (strcmp(dest,myrpt->name))
06730       {
06731          l = myrpt->links.next;
06732          /* see if this is one in list */
06733          while(l != &myrpt->links)
06734          {
06735             if (l->name[0] == '0') 
06736             {
06737                l = l->next;
06738                continue;
06739             }
06740             /* dont send back from where it came */
06741             if ((l == mylink) || (!strcmp(l->name,mylink->name)))
06742             {
06743                l = l->next;
06744                continue;
06745             }
06746             /* if it is, send it and we're done */
06747             if (!strcmp(l->name,dest))
06748             {
06749                /* send, but not to src */
06750                if (strcmp(l->name,src)) {
06751                   wf.data.ptr = str;
06752                   if (l->chan) ast_write(l->chan,&wf);
06753                }
06754                return;
06755             }
06756             l = l->next;
06757          }
06758       }
06759       /* if not for me, or is broadcast, redistribute to all links */
06760       if ((strcmp(dest,myrpt->name)) || (dest[0] == '*'))
06761       {
06762          l = myrpt->links.next;
06763          /* otherwise, send it to all of em */
06764          while(l != &myrpt->links)
06765          {
06766             if (l->name[0] == '0') 
06767             {
06768                l = l->next;
06769                continue;
06770             }
06771             /* dont send back from where it came */
06772             if ((l == mylink) || (!strcmp(l->name,mylink->name)))
06773             {
06774                l = l->next;
06775                continue;
06776             }
06777             /* send, but not to src */
06778             if (strcmp(l->name,src)) {
06779                wf.data.ptr = str;
06780                if (l->chan) ast_write(l->chan,&wf); 
06781             }
06782             l = l->next;
06783          }
06784       }
06785       /* if not for me, end here */
06786       if (strcmp(dest,myrpt->name) && (dest[0] != '*')) return;
06787       if (cmd[1] == '?')
06788       {
06789          time_t now;
06790          int n = 0;
06791 
06792          time(&now);
06793          if (myrpt->lastkeyedtime)
06794          {
06795             n = (int)(now - myrpt->lastkeyedtime);
06796          }
06797          sprintf(tmp1,"K %s %s %d %d",src,myrpt->name,myrpt->keyed,n);
06798          wf.data.ptr = tmp1;
06799          wf.datalen = strlen(tmp1) + 1;
06800          if (mylink->chan) ast_write(mylink->chan,&wf); 
06801          return;
06802       }
06803       if (myrpt->topkeystate != 1) return;
06804       rpt_mutex_lock(&myrpt->lock);
06805       for(i = 0; i < TOPKEYN; i++)
06806       {
06807          if (!strcmp(myrpt->topkey[i].node,src)) break;
06808       }
06809       if (i >= TOPKEYN)
06810       {
06811          for(i = 0; i < TOPKEYN; i++)
06812          {
06813             if (!myrpt->topkey[i].node[0]) break;
06814          }
06815       }
06816       if (i < TOPKEYN)
06817       {
06818          strncpy(myrpt->topkey[i].node,src,TOPKEYMAXSTR - 1);
06819          myrpt->topkey[i].timesince = ts;
06820          myrpt->topkey[i].keyed = seq;
06821       }
06822       rpt_mutex_unlock(&myrpt->lock);
06823       return;
06824    }
06825    if (tmp[0] == 'I')
06826    {
06827       /* XXX WARNING: be very careful with the limits on the folowing
06828        * sscanf() call, make sure they match the values defined above */
06829       if (sscanf(tmp,"%299s %299s %30x",cmd,src,&seq) != 3)
06830       {
06831          ast_log(LOG_WARNING, "Unable to parse ident string %s\n",str);
06832          return;
06833       }
06834       mdc1200_notify(myrpt,src,seq);
06835       strcpy(dest,"*");
06836    }
06837    else
06838    {
06839       /* XXX WARNING: be very careful with the limits on the folowing
06840        * sscanf() call, make sure they match the values defined above */
06841       if (sscanf(tmp,"%299s %299s %299s %30d %1c",cmd,dest,src,&seq,&c) != 5)
06842       {
06843          ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
06844          return;
06845       }
06846       if (strcmp(cmd,"D"))
06847       {
06848          ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
06849          return;
06850       }
06851    }
06852    if (dest[0] == '0')
06853    {
06854       strcpy(dest,myrpt->name);
06855    }     
06856 
06857    /* if not for me, redistribute to all links */
06858    if (strcmp(dest,myrpt->name))
06859    {
06860       l = myrpt->links.next;
06861       /* see if this is one in list */
06862       while(l != &myrpt->links)
06863       {
06864          if (l->name[0] == '0') 
06865          {
06866             l = l->next;
06867             continue;
06868          }
06869          /* dont send back from where it came */
06870          if ((l == mylink) || (!strcmp(l->name,mylink->name)))
06871          {
06872             l = l->next;
06873             continue;
06874          }
06875          /* if it is, send it and we're done */
06876          if (!strcmp(l->name,dest))
06877          {
06878             /* send, but not to src */
06879             if (strcmp(l->name,src)) {
06880                wf.data.ptr = str;
06881                if (l->chan) ast_write(l->chan,&wf);
06882             }
06883             return;
06884          }
06885          l = l->next;
06886       }
06887       l = myrpt->links.next;
06888       /* otherwise, send it to all of em */
06889       while(l != &myrpt->links)
06890       {
06891          if (l->name[0] == '0') 
06892          {
06893             l = l->next;
06894             continue;
06895          }
06896          /* dont send back from where it came */
06897          if ((l == mylink) || (!strcmp(l->name,mylink->name)))
06898          {
06899             l = l->next;
06900             continue;
06901          }
06902          /* send, but not to src */
06903          if (strcmp(l->name,src)) {
06904             wf.data.ptr = str;
06905             if (l->chan) ast_write(l->chan,&wf); 
06906          }
06907          l = l->next;
06908       }
06909       return;
06910    }
06911    if (myrpt->p.archivedir)
06912    {
06913       char dtmfstr[100];
06914 
06915       sprintf(dtmfstr,"DTMF,%s,%c",mylink->name,c);
06916       donodelog(myrpt,dtmfstr);
06917    }
06918    c = func_xlat(myrpt,c,&myrpt->p.outxlat);
06919    if (!c) return;
06920    rpt_mutex_lock(&myrpt->lock);
06921    if (c == myrpt->p.endchar) myrpt->stopgen = 1;
06922    if (myrpt->callmode == 1)
06923    {
06924       myrpt->exten[myrpt->cidx++] = c;
06925       myrpt->exten[myrpt->cidx] = 0;
06926       /* if this exists */
06927       if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
06928       {
06929          /* if this really it, end now */
06930          if (!ast_matchmore_extension(myrpt->pchannel,myrpt->patchcontext,
06931             myrpt->exten,1,NULL)) 
06932          {
06933             myrpt->callmode = 2;
06934             if(!myrpt->patchquiet)
06935             {
06936                rpt_mutex_unlock(&myrpt->lock);
06937                rpt_telemetry(myrpt,PROC,NULL); 
06938                rpt_mutex_lock(&myrpt->lock);
06939             }
06940          }
06941          else /* othewise, reset timer */
06942          {
06943             myrpt->calldigittimer = 1;
06944          }
06945       }
06946       /* if can continue, do so */
06947       if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
06948       {
06949          /* call has failed, inform user */
06950          myrpt->callmode = 4;
06951       }
06952    }
06953    if ((!myrpt->inpadtest) &&(c == myrpt->p.funcchar))
06954    {
06955       myrpt->rem_dtmfidx = 0;
06956       myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
06957       time(&myrpt->rem_dtmf_time);
06958       rpt_mutex_unlock(&myrpt->lock);
06959       return;
06960    } 
06961    else if (myrpt->rem_dtmfidx < 0)
06962    {
06963       if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
06964       {
06965          myrpt->mydtmf = c;
06966       }
06967       if (myrpt->p.propagate_dtmf) do_dtmf_local(myrpt,c);
06968       if (myrpt->p.propagate_phonedtmf) do_dtmf_phone(myrpt,mylink,c);
06969       rpt_mutex_unlock(&myrpt->lock);
06970       return;
06971    }
06972    else if (((myrpt->inpadtest) || (c != myrpt->p.endchar)) && (myrpt->rem_dtmfidx >= 0))
06973    {
06974       time(&myrpt->rem_dtmf_time);
06975       if (myrpt->rem_dtmfidx < MAXDTMF)
06976       {
06977          myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
06978          myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
06979          
06980          rpt_mutex_unlock(&myrpt->lock);
06981          strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1);
06982          res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink);
06983          rpt_mutex_lock(&myrpt->lock);
06984          
06985          switch(res){
06986 
06987             case DC_INDETERMINATE:
06988                break;
06989             
06990             case DC_REQ_FLUSH:
06991                myrpt->rem_dtmfidx = 0;
06992                myrpt->rem_dtmfbuf[0] = 0;
06993                break;
06994             
06995             
06996             case DC_COMPLETE:
06997             case DC_COMPLETEQUIET:
06998                myrpt->totalexecdcommands++;
06999                myrpt->dailyexecdcommands++;
07000                strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1);
07001                myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
07002                myrpt->rem_dtmfbuf[0] = 0;
07003                myrpt->rem_dtmfidx = -1;
07004                myrpt->rem_dtmf_time = 0;
07005                break;
07006             
07007             case DC_ERROR:
07008             default:
07009                myrpt->rem_dtmfbuf[0] = 0;
07010                myrpt->rem_dtmfidx = -1;
07011                myrpt->rem_dtmf_time = 0;
07012                break;
07013          }
07014       }
07015 
07016    }
07017    rpt_mutex_unlock(&myrpt->lock);
07018    return;
07019 }
07020 
07021 static void handle_link_phone_dtmf(struct rpt *myrpt, struct rpt_link *mylink,
07022    char c)
07023 {
07024 
07025 char  cmd[300];
07026 int   res;
07027 
07028    if (myrpt->p.archivedir)
07029    {
07030       char str[100];
07031 
07032       sprintf(str,"DTMF(P),%s,%c",mylink->name,c);
07033       donodelog(myrpt,str);
07034    }
07035    rpt_mutex_lock(&myrpt->lock);
07036 
07037    if (mylink->phonemode == 3) /*If in simplex dumb phone mode */
07038    {
07039       if(c == myrpt->p.endchar) /* If end char */
07040       {
07041          mylink->lastrealrx = 0; /* Keying state = off */
07042          rpt_mutex_unlock(&myrpt->lock);
07043          return;
07044       }
07045 
07046       if(c == myrpt->p.funcchar) /* If lead-in char */
07047       {
07048          mylink->lastrealrx = !mylink->lastrealrx; /* Toggle keying state */
07049          rpt_mutex_unlock(&myrpt->lock);
07050          return;
07051       }
07052    }
07053    else
07054    {
07055       if (c == myrpt->p.endchar)
07056       {
07057          if (mylink->lastrx)
07058          {
07059             mylink->lastrealrx = 0;
07060             rpt_mutex_unlock(&myrpt->lock);
07061             return;
07062          }
07063          myrpt->stopgen = 1;
07064          if (myrpt->cmdnode[0])
07065          {
07066             myrpt->cmdnode[0] = 0;
07067             myrpt->dtmfidx = -1;
07068             myrpt->dtmfbuf[0] = 0;
07069             rpt_mutex_unlock(&myrpt->lock);
07070             rpt_telemetry(myrpt,COMPLETE,NULL);
07071             return;
07072          }
07073       }
07074    }
07075    if (myrpt->cmdnode[0])
07076    {
07077       rpt_mutex_unlock(&myrpt->lock);
07078       send_link_dtmf(myrpt,c);
07079       return;
07080    }
07081    if (myrpt->callmode == 1)
07082    {
07083       myrpt->exten[myrpt->cidx++] = c;
07084       myrpt->exten[myrpt->cidx] = 0;
07085       /* if this exists */
07086       if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
07087       {
07088          /* if this really it, end now */
07089          if (!ast_matchmore_extension(myrpt->pchannel,myrpt->patchcontext,
07090             myrpt->exten,1,NULL)) 
07091          {
07092             myrpt->callmode = 2;
07093             if(!myrpt->patchquiet)
07094             {
07095                rpt_mutex_unlock(&myrpt->lock);
07096                rpt_telemetry(myrpt,PROC,NULL); 
07097                rpt_mutex_lock(&myrpt->lock);
07098             }
07099          }
07100          else /* othewise, reset timer */
07101          {
07102             myrpt->calldigittimer = 1;
07103          }
07104       }
07105       /* if can continue, do so */
07106       if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
07107       {
07108          /* call has failed, inform user */
07109          myrpt->callmode = 4;
07110       }
07111    }
07112    if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
07113    {
07114       myrpt->mydtmf = c;
07115    }
07116    if ((!myrpt->inpadtest) && (c == myrpt->p.funcchar))
07117    {
07118       myrpt->rem_dtmfidx = 0;
07119       myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
07120       time(&myrpt->rem_dtmf_time);
07121       rpt_mutex_unlock(&myrpt->lock);
07122       return;
07123    } 
07124    else if (((myrpt->inpadtest) || (c != myrpt->p.endchar)) && (myrpt->rem_dtmfidx >= 0))
07125    {
07126       time(&myrpt->rem_dtmf_time);
07127       if (myrpt->rem_dtmfidx < MAXDTMF)
07128       {
07129          myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
07130          myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
07131          
07132          rpt_mutex_unlock(&myrpt->lock);
07133          strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1);
07134          switch(mylink->phonemode)
07135          {
07136              case 1:
07137             res = collect_function_digits(myrpt, cmd, 
07138                SOURCE_PHONE, mylink);
07139             break;
07140              case 2:
07141             res = collect_function_digits(myrpt, cmd, 
07142                SOURCE_DPHONE,mylink);
07143             break;
07144              case 4:
07145             res = collect_function_digits(myrpt, cmd, 
07146                SOURCE_ALT,mylink);
07147             break;
07148              default:
07149             res = collect_function_digits(myrpt, cmd, 
07150                SOURCE_LNK, mylink);
07151             break;
07152          }
07153 
07154          rpt_mutex_lock(&myrpt->lock);
07155          
07156          switch(res){
07157 
07158             case DC_INDETERMINATE:
07159                break;
07160             
07161             case DC_DOKEY:
07162                mylink->lastrealrx = 1;
07163                break;
07164             
07165             case DC_REQ_FLUSH:
07166                myrpt->rem_dtmfidx = 0;
07167                myrpt->rem_dtmfbuf[0] = 0;
07168                break;
07169             
07170             
07171             case DC_COMPLETE:
07172             case DC_COMPLETEQUIET:
07173                myrpt->totalexecdcommands++;
07174                myrpt->dailyexecdcommands++;
07175                strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1);
07176                myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
07177                myrpt->rem_dtmfbuf[0] = 0;
07178                myrpt->rem_dtmfidx = -1;
07179                myrpt->rem_dtmf_time = 0;
07180                break;
07181             
07182             case DC_ERROR:
07183             default:
07184                myrpt->rem_dtmfbuf[0] = 0;
07185                myrpt->rem_dtmfidx = -1;
07186                myrpt->rem_dtmf_time = 0;
07187                break;
07188          }
07189       }
07190 
07191    }
07192    rpt_mutex_unlock(&myrpt->lock);
07193    return;
07194 }
07195 
07196 /* Doug Hall RBI-1 serial data definitions:
07197  *
07198  * Byte 0: Expansion external outputs 
07199  * Byte 1: 
07200  * Bits 0-3 are BAND as follows:
07201  * Bits 4-5 are POWER bits as follows:
07202  *    00 - Low Power
07203  *    01 - Hi Power
07204  *    02 - Med Power
07205  * Bits 6-7 are always set
07206  * Byte 2:
07207  * Bits 0-3 MHZ in BCD format
07208  * Bits 4-5 are offset as follows:
07209  *    00 - minus
07210  *    01 - plus
07211  *    02 - simplex
07212  *    03 - minus minus (whatever that is)
07213  * Bit 6 is the 0/5 KHZ bit
07214  * Bit 7 is always set
07215  * Byte 3:
07216  * Bits 0-3 are 10 KHZ in BCD format
07217  * Bits 4-7 are 100 KHZ in BCD format
07218  * Byte 4: PL Tone code and encode/decode enable bits
07219  * Bits 0-5 are PL tone code (comspec binary codes)
07220  * Bit 6 is encode enable/disable
07221  * Bit 7 is decode enable/disable
07222  */
07223 
07224 /* take the frequency from the 10 mhz digits (and up) and convert it
07225    to a band number */
07226 
07227 static int rbi_mhztoband(char *str)
07228 {
07229 int   i;
07230 
07231    i = atoi(str) / 10; /* get the 10's of mhz */
07232    switch(i)
07233    {
07234        case 2:
07235       return 10;
07236        case 5:
07237       return 11;
07238        case 14:
07239       return 2;
07240        case 22:
07241       return 3;
07242        case 44:
07243       return 4;
07244        case 124:
07245       return 0;
07246        case 125:
07247       return 1;
07248        case 126:
07249       return 8;
07250        case 127:
07251       return 5;
07252        case 128:
07253       return 6;
07254        case 129:
07255       return 7;
07256        default:
07257       break;
07258    }
07259    return -1;
07260 }
07261 
07262 /* take a PL frequency and turn it into a code */
07263 static int rbi_pltocode(char *str)
07264 {
07265 int i;
07266 char *s;
07267 
07268    s = strchr(str,'.');
07269    i = 0;
07270    if (s) i = atoi(s + 1);
07271    i += atoi(str) * 10;
07272    switch(i)
07273    {
07274        case 670:
07275       return 0;
07276        case 719:
07277       return 1;
07278        case 744:
07279       return 2;
07280        case 770:
07281       return 3;
07282        case 797:
07283       return 4;
07284        case 825:
07285       return 5;
07286        case 854:
07287       return 6;
07288        case 885:
07289       return 7;
07290        case 915:
07291       return 8;
07292        case 948:
07293       return 9;
07294        case 974:
07295       return 10;
07296        case 1000:
07297       return 11;
07298        case 1035:
07299       return 12;
07300        case 1072:
07301       return 13;
07302        case 1109:
07303       return 14;
07304        case 1148:
07305       return 15;
07306        case 1188:
07307       return 16;
07308        case 1230:
07309       return 17;
07310        case 1273:
07311       return 18;
07312        case 1318:
07313       return 19;
07314        case 1365:
07315       return 20;
07316        case 1413:
07317       return 21;
07318        case 1462:
07319       return 22;
07320        case 1514:
07321       return 23;
07322        case 1567:
07323       return 24;
07324        case 1622:
07325       return 25;
07326        case 1679:
07327       return 26;
07328        case 1738:
07329       return 27;
07330        case 1799:
07331       return 28;
07332        case 1862:
07333       return 29;
07334        case 1928:
07335       return 30;
07336        case 2035:
07337       return 31;
07338        case 2107:
07339       return 32;
07340        case 2181:
07341       return 33;
07342        case 2257:
07343       return 34;
07344        case 2336:
07345       return 35;
07346        case 2418:
07347       return 36;
07348        case 2503:
07349       return 37;
07350    }
07351    return -1;
07352 }
07353 
07354 /*
07355 * Shift out a formatted serial bit stream
07356 */
07357 
07358 static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data)
07359     {
07360 #ifdef __i386__
07361     int i,j;
07362     unsigned char od,d;
07363     static volatile long long delayvar;
07364 
07365     for(i = 0 ; i < 5 ; i++){
07366         od = *data++; 
07367         for(j = 0 ; j < 8 ; j++){
07368             d = od & 1;
07369             outb(d,myrpt->p.iobase);
07370        /* >= 15 us */
07371        for(delayvar = 1; delayvar < 15000; delayvar++); 
07372             od >>= 1;
07373             outb(d | 2,myrpt->p.iobase);
07374        /* >= 30 us */
07375        for(delayvar = 1; delayvar < 30000; delayvar++); 
07376             outb(d,myrpt->p.iobase);
07377        /* >= 10 us */
07378        for(delayvar = 1; delayvar < 10000; delayvar++); 
07379             }
07380         }
07381    /* >= 50 us */
07382         for(delayvar = 1; delayvar < 50000; delayvar++); 
07383