Caller ID Broadcaster

From MythTV Official Wiki
Jump to: navigation, search


Author Ken Bass
Description Caller ID Broadcaster
Supports


Caller ID Broadcaster - this program operates in one of two modes.

NOTE: Your modem must be caller ID capable (support the AT #CID=1) command and you must subscribe to the caller ID service from your telephone company.

I have tested this with 2 modems - 1) USR Sportster modem on /dev/ttyS0 and 2) a 'PCTel' 'winmodem' on /dev/ttyS15

1) [DEFAULT] It continuously monitors a caller ID capable modem. The calling info is extracted, wrapped in an XML structure, and UDP broadcast.

./cidbcast --file cidbcast.xml

By default, /dev/ttyS0 is used, the UDP port is 6947, and the broadcast address is 255.255.255.255. The default modem init string is "AT S7=45 S0=0 L0 V1 X4 &c1 E0 Q0 #CID=1 S41=1". All these can be overridden with command line options - invoke with '--help' for syntax.

NOTE: S41=1 is used to turn on distinctive ringing detection.

- OR - 2) [USING '--once' option] It relies on an external program to invoke it; a single UDP packet is sent based on the command line arguments, and the program exits. This mode would be used if you want to integrate into something like mgetty or for testing.

Example:

./cidbcast --once --file cidbcast.xml ttyS0 "3015551212" "JOE SCHMOE" 1
                                      ^^port ^^number    ^^name      ^^line

Rather then hardcode the XML structure, an xml 'template' must be provided. This program will replace keywords in the template with caller ID info. A default cidbcast.xml is provided.

Assumptions:

a) You are behind a firewall. You understand that the name/phone number of everyone calling you will be broadcast from your computer onto your LAN. Be sure your broadcast data is not be forwarded outside your local network.

b) You don't use your modem for other things (I use broadband - this modem is dedicated to this caller id task). However, if you integrate this with mgetty, this might not be a concern.

The following keywords will be replaced in the XML file before being sent via UDP.

%cid_port% - whatever tty port (ie; ttyS0, ttyS15, etc)
%cid_ring_type% - distinctive ring indicator (1 = Line 1, 2 = Line2,
                  3 = Line 3, 4 = Line 4)
%cid_name% - name of caller
%cid_number% - number of caller
%cid_date% - date from caller id (MMDD) [sent from phone company]
%cid_time% - time from caller id (HHMM) [sent from phone company]

The following fields are derived from the system this program is running on and not from the phone company. See 'localtime' manpage.

%cid_time_stamp% - number of seconds since Jan 1, 1970
%cid_time_zone% - timezone string (eg; EDT)
%cid_daylight_saving% - flag indicating if daylight savings is in effect
                        (positive value indicates daylight saving in effect,
                         zero indicates not in effect, negative value
                         indicates unknown)
%cid_time_hour%        - hour portion of time (0 to 23)
%cid_time_minute%      - minute portion of time (0 to 59)
%cid_time_second%      - second portion of time (0 to 59)
%cid_time_day%         - day of the month (1 to 31)
%cid_time_month%       - month (1 to 12)
%cid_time_year%        - year (actual year)
%cid_time_string%      - string representation (see ctime())
                       - example: Wed Sep 17 22:48:21 2003

The phonebook argument allows cidbcast to supply names when the telco doesn't transmit one which is very handy in my area where few/no mobile numbers have names associated with them. Substitutions are made just before expanding the keywords in the xml file. The phonebook parsing routine is very simplistic - it expects a number, a colon and then a name with no whitespace before or after. The program has a hard limit of 19 characters for names.


Important.png Note: See also the MythTV UDP Relay program.


Script.png cidbcast.c

/*
** Caller ID UDP broadcaster
**
** Ken Bass ( kbass a kenbass d com ) 9/13/03
**
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <termios.h>

typedef struct
{
  char ring_line[5];
  char date[5];
  char time[5];
  char number[20];
  char name[20];
} CID_Info;

int checkfor_cid_info(char *buffer, int len, CID_Info *cid_info);
int checkfor_two_rings(char *buffer, int len);

typedef struct
{
  char *name;
  char *number;
} phonebook_entry;

phonebook_entry *phonebook = NULL;
int phonebook_size = 0;

void read_phonebook(const char *filename)
{
  FILE *pb = fopen(filename, "r");
  if (pb == NULL)
  {
    fprintf(stderr, "Unable to open phonebook file %s\n", filename);
    exit(1);
  }
  fseek(pb, 0L, SEEK_END);
  int pbsize = ftell(pb);
  fseek(pb, 0L, SEEK_SET);
  char *pbtext = (char *) malloc(pbsize+1);
  fread(pbtext, 1, pbsize, pb);
  fclose(pb);

  int num_entries = 0;
  char *cp;
  for (cp = pbtext; *cp != '\0'; cp++)
    if (*cp == '\n') num_entries++;
  if (num_entries == 0) return;

  phonebook = (phonebook_entry *) malloc(num_entries * sizeof(phonebook_entry));
  int i;
  for (i = 0; i < num_entries; i++)
  {
    if ((phonebook[i].number = strtok(pbtext, ":")) == NULL) return;
    pbtext = NULL;
    if ((phonebook[i].name = strtok(pbtext, "\n")) == NULL) return;
    phonebook_size++;
  }
}

void match_in_phonebook(CID_Info *cid_info)
{
  if (phonebook == NULL) return;
  int i;
  for (i = 0; i < phonebook_size; i++)
  {
    if (strcmp(cid_info->number, phonebook[i].number) == 0)
    {
      strcpy(cid_info->name, phonebook[i].name);
      return;
    }
  }
}

int verbose = 0;
char xml_filename[255];
char bcast_addr[16];
int bcast_addr_hex=-1;
char serial_port[16];
int udp_port;
char modem_init_string[255];

int ipaddr_to_int(const char *stringIP, unsigned int *ip)
{
  int a,b,c,d;
  
  if (4 != sscanf(stringIP, "%d.%d.%d.%d", &a, &b, &c, &d))
    {
      return -1;
    }
  
  *ip = ((a&0xFF) << 24) +
    ((b&0xFF) << 16) +
    ((c&0xFF) <<  8) +
    (d&0xFF);
  
  return 0;
}

char *get_tail(char *string)
{
  char *c;
  c = string+strlen(string);
  int found = 0;
  while(c!=string)
  {
    if (*c=='/')
    {
      found = 1;
      c++;
      break;
    }
    c--;
  }

  return c;
}

char *replace(char *in_string, char *out_string, char* key_string, char *replace_string)
{
  int str_index, outstring_idx, key_idx, end, replace_len, key_len, cpy_len;
  char *c;
  
  if ((c = (char *) strstr(in_string, key_string)) == NULL)
  {
    strcpy(out_string, in_string);
    return out_string;
  }
  
  replace_len = strlen(replace_string);
  key_len = strlen(key_string);
  end = strlen(in_string) - key_len;
  key_idx = c - in_string;
  
  outstring_idx = 0;
  str_index = 0;
  while(str_index <= end && c != NULL)
  {
    /* Copy characters from the left of matched pattern occurrence */
    cpy_len = key_idx-str_index;
    strncpy(out_string+outstring_idx, in_string+str_index, cpy_len);
    outstring_idx += cpy_len;
    str_index += cpy_len;
    
    /* Copy replacement characters instead of matched pattern */
    strcpy(out_string+outstring_idx, replace_string);
    outstring_idx += replace_len;
    str_index += key_len;
    
    /* Check for another pattern match */
    if ((c = (char *) strstr(in_string+str_index, key_string)) != NULL)
    {
      key_idx = c - in_string;
    }
  }

  /* Copy remaining characters from the right of last matched pattern */
  strcpy(out_string+outstring_idx, in_string+str_index);
  
  return out_string; 
}

int open_modem()
{
  int            fd;
  struct termios options;
  
  /* open the port */
  fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
  fcntl(fd, F_SETFL, 0);
  
  /* get the current options */
  tcgetattr(fd, &options);
  
  /* set raw input, 1 second timeout */
  cfsetospeed(&options, B38400);
  cfsetispeed(&options, B38400); 
  options.c_cflag     |= (CLOCAL | CREAD);
  options.c_lflag     &= ~(ICANON | ECHO | ECHOE | ISIG);
  options.c_oflag     &= ~OPOST;
  options.c_cc[VMIN]  = 0;
  options.c_cc[VTIME] = 10;
  
  /* set the options */
  tcsetattr(fd, TCSANOW, &options);

  return fd;
}

int issue_modem_cmd(int fd, char *cmd_string)
{
 char buffer[255];  /* Input buffer */
 char *bufptr;      /* Current char in buffer */
 int  nbytes;       /* Number of bytes read */
 int  tries;        /* Number of tries so far */
 
 memset(buffer, 0, 255);

 for (tries = 0; tries < 3; tries ++)
 {

   if (verbose)
   {
     printf("Send %s\n", cmd_string);
   }

   if (write(fd, cmd_string, strlen(cmd_string)) < strlen(cmd_string))
   {
     printf("AT send failed on try %d\n", tries);
     continue;
   }
   
   /* read characters into our string buffer until we get a CR or NL */
   bufptr = buffer;
   while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
     {
       bufptr += nbytes;
       if (bufptr[-1] == '\n' || bufptr[-1] == '\r')
	 break;
     }

   /* nul terminate the string and see if we got an OK response */
   *bufptr = '\0';

   if (strstr(buffer, "OK") != 0)
     return (0);

   sleep(1);
 }
 
 return (-1);
}

int waitfor_ring(int fd, char *wait_string, CID_Info *cid_info)
{
 char buffer[255];  /* Input buffer */
 char *bufptr;      /* Current char in buffer */
 int  nbytes;       /* Number of bytes read */
 fd_set mrd_set;
 int rv;
 struct timeval tv;
 struct timeval *tv_p;
 memset(buffer, 0, 255);
 int first = 0;

 FD_ZERO(&mrd_set);
 FD_SET (fd, &mrd_set);

 /* read characters into our string buffer until we get a CR or NL */
 bufptr = buffer;

 while (1)
   {
     /* Wait up to ten seconds. */
     if (first == 0)
     {
       tv_p = NULL;
       first = 1;
     }
     else
     {
       tv.tv_sec = 10;
       tv.tv_usec = 0;
       tv_p = &tv;
     }

     /* Keep this in mind - this select will be invoked as characters
     ** are received from the serial device. There is no guarantee how
     ** many characters will be received on each 'read'. Therefore
     ** each time we read characters we append them to the tail end of
     ** the buffer. checkfor_cid_info() below will check the buffer for
     ** complete caller ID info.
     */
     
     rv = select(fd+1, &mrd_set, NULL, NULL, tv_p);
     if (rv < 0)
       {
	 printf("Select failed\n");
	 return -1;
       }
     else if (rv == 0)
       {
	 if (verbose)
	 {
	   printf("Timed out\n");
	 }
	 return -1;
       }
     
     if (FD_ISSET(fd, &mrd_set))
       {
	 while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
	   {
	     bufptr += nbytes;
	   }
	 
	 /* nul terminate the string and see if we got a complete response */
	 *bufptr = '\0';
	 
	 if (checkfor_cid_info(buffer, 255,
			       cid_info))
	   {
	     return 0;
	   }

	 /* If we encounter 2 rings we never got caller ID info*/
	 if (checkfor_two_rings(buffer, 255))
	   {
	     return -1;
	   }
       }
   }

   return (-1);
}

int checkfor_two_rings(char *buffer, int len)
{
  char *bptr;
  char *c;
  char key[6] = "\nRING";

  if ((c = (char *) strstr(buffer, key)) == NULL)
  {
    if (verbose)
    {
      printf("Found ZERO RINGS\n");
    }
    return 0;
  }

  bptr = c+strlen(key);
  if ((c = (char *) strstr(bptr, key)) == NULL)
  {
    if (verbose)
    {
      printf("Found ONE RINGS\n");
    }
    return 0;
  }
  else
  {
    if (verbose)
    {
      printf("Found TWO RINGS\n");
    }
    return 1;
  }

}

char *extract_cid_field(char *buffer, int len, char *field_name, int *field_len)
{
  char *bptr, *eptr;
  char *c;
  int complete;

  if ((c = (char *) strstr(buffer, field_name)) == NULL)
  {
    if (verbose)
    {
      printf("%s not Found\n", field_name);
    }
    return 0;
  }

  bptr = c+strlen(field_name);
  eptr = bptr;
  complete = 0;
  while (eptr != 0)
  {
    if ((*eptr == '\r' || *eptr == '\n'))
    {
      complete = 1;
      break;
    }
    eptr++;
  }

  if (complete)
  {
    *field_len = eptr - bptr;
    return bptr;
  }
  else
  {
    *field_len = 0;
    return NULL;
  }
}

int getNum(char *dest, int dsize, const char *src, int ssize)
{
  int ret = 0;

  if ( dest && src )
  {
    int i, j;
    memset(dest, 0, dsize);
    for(i=0,j=0; i<ssize && i<dsize; i++)
    {
      if ( isspace(src[i]) )
      {
        break;
      }
      else if ( isdigit(src[i]) )
      {
        dest[j++] = src[i];
      }
    }
    ret = j;
  }

  return ret;
}

/*
** Data comes from modem (when enabled via AT#CID=1) as
** DATE = 0915
** TIME = 1700
** NMBR = 3015551212
** NAME = SCHMOE JOE
**
** For the ZyXel 1496E+
** TIME: 09-15 17:00
** CALLER NAME: SCHMOE JOE
** CALLER NUMBER: 3015551212
**
** init string is: "AT E0 L0 M0 N0 Q0 V1 X7 &C1 &H3 S0=0 S7=45 S13.2=1 S40.2=1 S40.3=1 S40.4=1"
*/

int checkfor_cid_info(char *buffer, int len, CID_Info *cid_info)
{
  char *field_ptr, date[8], time[8];
  int field_len, time_len = 0;

  if ( (field_ptr = extract_cid_field(buffer, len, "NAME = ", &field_len)) 
   || (field_ptr = extract_cid_field(buffer, len, "CALLER NAME: ", &field_len)) )
  {
    memset(cid_info->name, 0, sizeof(cid_info->name));
    memcpy(cid_info->name, field_ptr, field_len);
    if (verbose)
    {
      printf("name %s\n",cid_info->name);
    }
  }
  else
  {
    if (verbose)
    {
      printf("NAME not found\n");
    }
    return 0;
  }

  if ( (field_ptr = extract_cid_field(buffer, len, "NMBR = ", &field_len))
   || (field_ptr = extract_cid_field(buffer, len, "CALLER NUMBER: ", &field_len)) )
  {
    memset(cid_info->number, 0, sizeof(cid_info->number));
    memcpy(cid_info->number, field_ptr, field_len);
    if (verbose)
    {
      printf("number %s\n",cid_info->number);
    }
  }
  else
  {
    if (verbose)
    {
      printf("NMBR not found\n");
    }
    return 0;
  }

  if ( (field_ptr = extract_cid_field(buffer, len, "TIME: ", &field_len)) )
  {
    int date_len;
    date_len = getNum(date, sizeof(date), field_ptr, field_len);
    time_len = getNum(time, sizeof(time), &field_ptr[date_len+2], field_len-date_len-2);
    field_ptr = &date[0];
    field_len = date_len;
    if ( verbose )
    {
      printf( "Date(%d): %s\nTime(%d): %s\nField_ptr: %s\n",
        field_len, date, time_len, time, field_ptr );
    }
  }
  else
  {
    field_ptr = extract_cid_field(buffer, len, "DATE = ", &field_len);
  }
  if (field_ptr)
  {
    memset(cid_info->date, 0, sizeof(cid_info->date));
    memcpy(cid_info->date, field_ptr, field_len);
    if (verbose)
    {
      printf("date %s\n",cid_info->date);
    }
  }
  else
  {
    if (verbose)
    {
      printf("DATE not found\n");
    }
    return 0;
  }

  if (time_len)
  {
    field_ptr = &time[0];
    field_len = time_len;
  }
  else
  {
    field_ptr = extract_cid_field(buffer, len, "TIME = ", &field_len);
  }
  if (field_ptr)
  {
    memset(cid_info->time, 0, sizeof(cid_info->time));
    memcpy(cid_info->time, field_ptr, field_len);
    if (verbose)
    {
      printf("time %s\n",cid_info->time);
    }
  }
  else
  {
    if (verbose)
    {
      printf("TIME not found\n");
    }
    return 0;
  }

  field_ptr = extract_cid_field(buffer, len, "RING", &field_len);
  if (field_ptr)
  {
    memset(cid_info->ring_line, 0, sizeof(cid_info->ring_line));
    memcpy(cid_info->ring_line, field_ptr, field_len);
    if (strcmp(cid_info->ring_line, " A") == 0)
    {
      strcpy(cid_info->ring_line, "1");
    }
    else if (strcmp(cid_info->ring_line, " B") == 0)
    {
      strcpy(cid_info->ring_line, "2");
    }
    else if (strcmp(cid_info->ring_line, " C") == 0)
    {
      strcpy(cid_info->ring_line, "3");
    }
    else if (strcmp(cid_info->ring_line, " D") == 0)
    {
      strcpy(cid_info->ring_line, "4");
    }
    else
    {
      strcpy(cid_info->ring_line, "1");
    }

    if (verbose)
    {
      printf("ring_line %s\n",cid_info->ring_line);
    }
  }
  else
  {
    if (verbose)
    {
      printf("RING not found\n");
    }
    return 0;
  }

  return 1;
}

int close_modem(int fd)
{
  close(fd);

  return 0;

}

int init_modem(int *fd)
{

  *fd = open_modem();

  if (*fd <= 0)
  {
    printf("error open modem\n");
    return -1;
  }

  if (write(*fd, "+++", 3) != 3)
  {
    printf("error init modem\n");
    return -1;
  }

  sleep(2);
  
  if (issue_modem_cmd(*fd, "AT\r") < 0)
  {
    printf("error init modem\n");
    return -1;
  }
  
  if (issue_modem_cmd(*fd, modem_init_string) < 0)
  {
    printf("error init modem\n");
    return -1;
  }

  return 0;  
}

void print_help(char *progname)
{
  printf("\nUsage: %s [--once] --file=xyz.xml [OPTIONS]\n", progname);
  printf("A caller id UDP broadcast utility.\n\n");
  printf("  -d, --device    : serial device (--device=/dev/ttyS0)\n");
  printf("  -u, --udpport   : UDP port to broadcase (--udpport=6947)\n");
  printf("  -b, --bcast     : UDP broadcast address (--bcast=255.255.255.255)\n");
  printf("  -f, --file      : (REQUIRED)XML file to use as a template (--file=cidbcast.xml)\n");
  printf("  -i, --init      : Modem init string (--init=\"AT S7=45 S0=0 L0 V1 X4 &c1 E0 Q0 #CID=1 S41=1\")\n");
  printf("  -l, --logfile   : Append stdout and stderr (--logfile=/var/log/cibcast.log)\n");
  printf("  -p, --phonebook : File of NUMBER:NAME lines used to supply/override caller names\n");
  printf("  -v, --verbose   : some debug stuff\n");

  printf("\n  -o, --once      : runs program in 'single shot' mode (see README)\n");
  printf("  (if --once): --file=cidbcast.xml [port] [number] [name] [line]\n");

}

int main(int argc, char *argv[])
{
    struct sockaddr_in addr;
    int fd;
    int yes = 1;
    char* raw_message;
    char* message;
    char* tmp1_message;
    char* tmp2_message;
    struct timeb time;
    struct tm* timeValues;
    FILE *fptr;
    long fsize;
    CID_Info cid_info;
    int modem_fd;
    int done = 0;
    int rv;
    int option_index = 0, c;
    int file_arg_found = 0;
    int single_shot = 0;
    char string[255];

    static struct option long_options[] = 
      {
	{"device", required_argument, 0, 'd'},
	{"udpport", required_argument, 0, 'u'},
	{"bcast", required_argument, 0, 'b'},
	{"file", required_argument, 0, 'f'},
	{"logfile", required_argument, 0, 'l'},
	{"phonebook", required_argument, 0, 'p'},
	{"init", required_argument, 0, 'i'},
	{"help", no_argument, 0, 'h'},
	{"verbose", no_argument, 0, 'v'},
	{"once", no_argument, 0, 'o'},
	{0, 0, 0, 0}
      };

    memset(&cid_info, 0, sizeof(cid_info));

    /* Defaults */
    strcpy(xml_filename, "");
    strcpy(bcast_addr, "255.255.255.255");
    ipaddr_to_int(bcast_addr, &bcast_addr_hex);
    strcpy(serial_port, "/dev/ttyS0");
    strcpy(modem_init_string, "AT S7=45 S0=0 L0 V1 X4 &c1 E0 Q0 #CID=1 S41=1\r");
    udp_port = 6947;

    while (1)
    {
      c = getopt_long (argc, argv, "t:p:b:f:si:hvo",
		       long_options, &option_index);
      if (c == -1)
	break;
      
      switch (c) 
      {

      case 'd':
	strncpy(serial_port, optarg, sizeof(serial_port)-1);
	break;

      case 'i':
	strncpy(modem_init_string, optarg, sizeof(modem_init_string)-2);
	modem_init_string[strlen(modem_init_string)] = '\r';
	break;

      case 'u':
	udp_port = atoi(optarg);
	break;

      case 'b':
	strncpy(bcast_addr, optarg, sizeof(bcast_addr));
	if (ipaddr_to_int(bcast_addr, &bcast_addr_hex) < 0)
	{
	  printf("Error in Broadcast address %s\n",
		 bcast_addr);
	  exit(1);
	}
	break;

      case 'f':
	strncpy(xml_filename, optarg, sizeof(xml_filename)-1);
	file_arg_found = 1;
	break;

      case 'l':
        // redirect output immediately so arg errors will be logged
        if (freopen(optarg, "a", stdout) == (FILE *) NULL ||
            dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
        {
            fprintf(stderr, "Unable to open logfile %s\n", optarg);
            exit(1);
        }
        setbuf(stdout, NULL);
	break;

      case 'p':
        read_phonebook(optarg);
	break;

      case 'h':
	print_help(argv[0]);
	exit(0);
	break;	

      case 'v':
	verbose = 1;
	printf("Verbose mode enabled\n");
	break;

      case 'o':
	printf("Single shot mode enabled - sending UDP and exiting\n");
	single_shot = 1;
	break;

      default:
	print_help(argv[0]);
	exit(0);
      }
    }

    if (optind < argc) {
      if (single_shot)
	{
	  strcpy(serial_port, argv[optind++]);
	  strcpy(cid_info.number, argv[optind++]);
	  strcpy(cid_info.name, argv[optind++]);
	  strcpy(cid_info.ring_line, argv[optind++]);
	  strcpy(cid_info.date, "");
	  strcpy(cid_info.time, "");
	}
    }

    if (!file_arg_found)
    {
      print_help(argv[0]);
      return -1;
    }

    fptr=fopen(xml_filename, "r");
    if (fptr == NULL)
    {
      printf("Could not open xml file %s - exiting\n", xml_filename);
      exit(1);
    }	

    /* Goto end of file, get file size, rewind */
    (void)fseek(fptr, 0L, SEEK_END);
    fsize = ftell(fptr);
    (void)fseek(fptr, 0L, SEEK_SET);

    /* construct message */
    raw_message = (char *)malloc(fsize+1);
    tmp1_message = (char *)malloc(fsize+1024);
    tmp2_message = (char *)malloc(fsize+1024);
    message = (char *)malloc(fsize+1024);

    fread(raw_message, 1, fsize, fptr);
    if (verbose)
    {
      printf("raw_message (%lu):\n%s", fsize, raw_message);
    }
    fclose(fptr);

    if (!single_shot)
    {
      if (init_modem(&modem_fd) < 0)
      {
	printf("Error initializing modem %s - exiting\n", serial_port);
	exit(1);
      }
      else
      {
	printf("Modem %s initialized OK - waiting for RING\n", serial_port);
      }
    }

    while (!done)
    {
      if (!single_shot)
      {
	rv = waitfor_ring(modem_fd, "RING", &cid_info);
      }
      else
      {
	rv = 0;
      }

      if (rv == 0)
      {

	if (single_shot)
	{
	  done = 1;
	}

	ftime(&time);
	timeValues = localtime(&(time.time));

        match_in_phonebook(&cid_info);

        /* USR modems use name 0 when caller is unidentified */
        if (strcmp(cid_info.name, "0") == 0)
        {
          strcpy(cid_info.name, "Unknown");
        }

	replace(raw_message, tmp2_message, "%cid_number%", cid_info.number);
	replace(tmp2_message, tmp1_message, "%cid_name%", cid_info.name);
	replace(tmp1_message, tmp2_message, "%cid_date%", cid_info.date);
	replace(tmp2_message, tmp1_message, "%cid_time%", cid_info.time);
	replace(tmp1_message, tmp2_message, "%cid_ring_type%", cid_info.ring_line);
	replace(tmp2_message, tmp1_message, "%cid_port%", 
		get_tail(serial_port));
	sprintf(string, "%d", (int)time.time);
	replace(tmp1_message, tmp2_message, "%cid_time_stamp%", string);
	sprintf(string, "%s", tzname[1]);
	replace(tmp2_message, tmp1_message, "%cid_time_zone%", string);
	sprintf(string, "%d", timeValues->tm_isdst);
	replace(tmp1_message, tmp2_message, "%cid_daylight_saving%", string);
	sprintf(string, "%d", timeValues->tm_hour);
	replace(tmp2_message, tmp1_message, "%cid_time_hour%", string);
	sprintf(string, "%d", timeValues->tm_min);
	replace(tmp1_message, tmp2_message, "%cid_time_minute%", string);
	sprintf(string, "%d", timeValues->tm_sec);
	replace(tmp2_message, tmp1_message, "%cid_time_second%", string);
	sprintf(string, "%d", timeValues->tm_mday);
	replace(tmp1_message, tmp2_message, "%cid_time_day%", string);
	sprintf(string, "%d", timeValues->tm_mon + 1);
	replace(tmp2_message, tmp1_message, "%cid_time_month%", string);
	sprintf(string, "%d", timeValues->tm_year + 1900);
	replace(tmp1_message, tmp2_message, "%cid_time_year%", string);
	replace(tmp2_message, tmp1_message, "%cid_time_string%", ctime(&(time.time)));
	strcpy(message, tmp1_message);

	if (verbose)
	{
	  printf("\nmessage (%u):\n%s", strlen(message), message);
	}
	
	/* create what looks like an ordinary UDP socket */
	if ((fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) 
	{
	  perror("socket");
	  exit(1);
	}
	
	/* setsockopt */
	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(int) ) < 0) 
	  {
	    perror("bind");
	    exit(1);
	  }
	
	/* set up destination address */
	memset(&addr,0,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr=htonl(bcast_addr_hex);
	addr.sin_port=htons(udp_port);
	
	/* now just sendto() our destination! */
	if (sendto(fd,message,strlen(message),0,(struct sockaddr *) &addr,
		   sizeof(addr)) < 0)
	{
	  perror("sendto");
	}
	else
	{
	  printf("Sent UDP/XML packet to IP %s port %d\n", bcast_addr, udp_port);
	}
	close (fd);
      }
      else
	{
	  if (verbose)
	    {
	      printf("Error getting CID_info, dont send anything\n");
	    }
	}
    }

    if (!single_shot)
    {
      close_modem(modem_fd);
    }

    return 0;
}


Script.png cidbcast.xml

<telephone-event type="callerID" TTY="%cid_port%">
  <time stamp="%cid_time_stamp%" zone="%cid_time_zone%" daylight-savings="%cid_daylight_saving%" second="%cid_time_second%" minute="%cid_time_minute%" hour="%cid_time_hour%" day="%cid_time_day%" month="%cid_time_month%" year="%cid_time_year%">%cid_time_string%</time>
  <ring-type>%cid_ring_type%</ring-type>
  <from>
    <name>%cid_name%</name>
    <number>%cid_number%</number>
  </from>
</telephone-event>


Script.png Makefile

#################################################################################
#  
#  makefile
#
#################################################################################

CC       =  gcc
LINK     =  gcc
RM       =  rm 
RM_FLAGS =  -f

CFLAGS  	= -g -Wall -O
CDEFS   	= -DDEBUG

# Libraries

STDLIBS    = -lm
X11LIBS    =  -L/usr/X11R6/lib -lX11
PROGRAM    = cidbcast

#################################################################################

.SUFFIXES: .o .c .flex
.c.o:   ;       $(CC)  $(CFLAGS) $(CDEFS) -c $< -o $*.o

cidbcast: cidbcast.o
	$(LINK) -o cidbcast cidbcast.o $(STDLIBS)

#################################################################################

clean:
	$(RM) $(RM_FLAGS) $(PROGRAM) *.o *~ core

#################################################################################