00001
00002
00003
00004
00005
00006
00007 #include <sys/types.h>
00008 #include <sys/socket.h>
00009 #include <netinet/in.h>
00010 #include <arpa/inet.h>
00011 #include <string.h>
00012 #include <stdio.h>
00013 #include <sys/timeb.h>
00014 #include <sys/types.h>
00015 #include <sys/stat.h>
00016 #include <fcntl.h>
00017 #include <unistd.h>
00018 #include <stdlib.h>
00019 #include <getopt.h>
00020 #include <termios.h>
00021 #include <libxml/tree.h>
00022 #include <libxml/parser.h>
00023 #include <libxml/xmlmemory.h>
00024 #include <libxml/debugXML.h>
00025 #include <libxml/HTMLtree.h>
00026 #include <libxml/xmlIO.h>
00027 #include <libxml/DOCBparser.h>
00028 #include <libxml/xinclude.h>
00029 #include <libxml/catalog.h>
00030 #include <libxslt/xslt.h>
00031 #include <libxslt/xsltInternals.h>
00032 #include <libxslt/transform.h>
00033 #include <libxslt/xsltutils.h>
00034
00035 #define UDP_RCV_BUFSIZE (64*1024)
00036
00037 int verbose = 0;
00038 char xml_filename[255];
00039
00040 typedef struct
00041 {
00042 int fd;
00043 int port;
00044 struct sockaddr_in addr;
00045 char bcast_addr[16];
00046 int bcast_addr_hex;
00047 } SOCKET_HANDLE;
00048
00049 typedef struct
00050 {
00051
00052 SOCKET_HANDLE rcv_sock_handle;
00053 SOCKET_HANDLE snd_sock_handle;
00054 char udp_rcv_buffer[UDP_RCV_BUFSIZE];
00055 char xml_output_buffer[UDP_RCV_BUFSIZE];
00056 char xslt_filename[255];
00057 } GLOBALS;
00058
00059 int ipaddr_to_int(const char *stringIP, unsigned int *ip)
00060 {
00061 int a,b,c,d;
00062
00063 if (4 != sscanf(stringIP, "%d.%d.%d.%d", &a, &b, &c, &d))
00064 {
00065 return -1;
00066 }
00067
00068 *ip = ((a&0xFF) << 24) +
00069 ((b&0xFF) << 16) +
00070 ((c&0xFF) << 8) +
00071 (d&0xFF);
00072
00073 return 0;
00074 }
00075
00076 int setup_rcv_socket(SOCKET_HANDLE* sock, int port)
00077 {
00078 int yes = 1;
00079 sock->port = port;
00080
00081
00082 if ( (sock->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
00083 {
00084 perror("socket");
00085 return -1;
00086 }
00087
00088
00089 sock->addr.sin_family = AF_INET;
00090 sock->addr.sin_addr.s_addr = htonl(INADDR_ANY);
00091 sock->addr.sin_port = htons(port);
00092
00093
00094 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int) ) < 0)
00095 {
00096 perror("setsockopt");
00097 return -2;
00098 }
00099
00100
00101 if ( bind(sock->fd, (struct sockaddr*) &(sock->addr), sizeof(sock->addr)) < 0 )
00102 {
00103 perror("bind");
00104 return -3;
00105 }
00106
00107 return 0;
00108 }
00109
00110 int setup_snd_socket(SOCKET_HANDLE* sock, int port, char *addr)
00111 {
00112 int yes = 1;
00113
00114 strcpy(sock->bcast_addr, addr);
00115 if (ipaddr_to_int(sock->bcast_addr, &sock->bcast_addr_hex) < 0)
00116 {
00117 printf("Error in Broadcast address %s\n",
00118 sock->bcast_addr);
00119 return -1;
00120 }
00121
00122 sock->port = port;
00123
00124
00125 if ( (sock->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
00126 {
00127 perror("socket");
00128 return -1;
00129 }
00130
00131
00132 sock->addr.sin_family = AF_INET;
00133 sock->addr.sin_addr.s_addr = htonl(sock->bcast_addr_hex);
00134 sock->addr.sin_port = htons(sock->port);
00135
00136
00137 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int) ) < 0)
00138 {
00139 perror("setsockopt");
00140 return -2;
00141 }
00142
00143
00144 if (setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(int) ) < 0)
00145 {
00146 perror("so_broadcast");
00147 return -3;
00148 }
00149
00150 return 0;
00151 }
00152
00153 int send_xml_output(GLOBALS *globals, SOCKET_HANDLE* sock)
00154 {
00155 int rc;
00156
00157
00158 rc = sendto(sock->fd,
00159 globals->xml_output_buffer,
00160 strlen(globals->xml_output_buffer),0,(struct sockaddr *) &sock->addr,
00161 sizeof(sock->addr));
00162 if (rc < 0)
00163 {
00164 perror("sendto");
00165 return -1;
00166 }
00167 else
00168 {
00169 if (verbose)
00170 {
00171 printf("Sent UDP/XML packet to IP %s port %d (%d bytes)\n",
00172 sock->bcast_addr, sock->port, rc);
00173 }
00174 }
00175
00176 return 0;
00177
00178 }
00179
00180
00181 int process_udp_xml(GLOBALS *globals)
00182 {
00183 xsltStylesheetPtr cur = NULL;
00184 xmlDocPtr doc, res;
00185 xmlChar *xml_output;
00186 int xml_output_len;
00187 int rc;
00188
00189 xmlSubstituteEntitiesDefault(1);
00190 xmlLoadExtDtdDefaultValue = 1;
00191 cur = xsltParseStylesheetFile((const xmlChar *)globals->xslt_filename);
00192 if (cur == NULL)
00193 {
00194 xsltCleanupGlobals();
00195 xmlCleanupParser();
00196 return -1;
00197 }
00198
00199 doc = xmlParseMemory(globals->udp_rcv_buffer, UDP_RCV_BUFSIZE);
00200 if (doc == NULL)
00201 {
00202 xsltFreeStylesheet(cur);
00203 xsltCleanupGlobals();
00204 xmlCleanupParser();
00205 return -1;
00206 }
00207 res = xsltApplyStylesheet(cur, doc, NULL);
00208 if (res == NULL)
00209 {
00210 xmlFreeDoc(res);
00211 xsltFreeStylesheet(cur);
00212 xsltCleanupGlobals();
00213 xmlCleanupParser();
00214 return -1;
00215 }
00216
00217 rc = xsltSaveResultToString(&xml_output,
00218 &xml_output_len,
00219 res,
00220 cur);
00221
00222
00223
00224 if ((rc < 0) || (xml_output_len == 0))
00225 {
00226
00227 xsltFreeStylesheet(cur);
00228 xmlFreeDoc(res);
00229 xmlFreeDoc(doc);
00230
00231 xsltCleanupGlobals();
00232 xmlCleanupParser();
00233
00234 return -1;
00235 }
00236
00237 strncpy(globals->xml_output_buffer, xml_output, UDP_RCV_BUFSIZE-1);
00238
00239 xmlFree(xml_output);
00240
00241 xsltFreeStylesheet(cur);
00242 xmlFreeDoc(res);
00243 xmlFreeDoc(doc);
00244
00245 xsltCleanupGlobals();
00246 xmlCleanupParser();
00247
00248 return 0;
00249 }
00250
00251 int waitfor_udp(GLOBALS *globals, SOCKET_HANDLE* sock)
00252 {
00253 int nbytes;
00254 fd_set udp_rd_set;
00255 int rv;
00256
00257 FD_ZERO(&udp_rd_set);
00258 FD_SET (sock->fd, &udp_rd_set);
00259
00260 memset (globals->udp_rcv_buffer, 0, UDP_RCV_BUFSIZE);
00261
00262 rv = select(sock->fd+1, &udp_rd_set, NULL, NULL, NULL);
00263 if (rv < 0)
00264 {
00265 printf("Select failed\n");
00266 return -1;
00267 }
00268
00269 if (FD_ISSET(sock->fd, &udp_rd_set))
00270 {
00271 nbytes = read(sock->fd, globals->udp_rcv_buffer, UDP_RCV_BUFSIZE);
00272 if (nbytes <= 0)
00273 {
00274 return -1;
00275 }
00276 else
00277 {
00278 if (verbose)
00279 {
00280 printf("got UDP data (%d bytes)\n", nbytes);
00281 }
00282 }
00283 }
00284
00285 return 0;
00286 }
00287
00288 void print_help(char *progname)
00289 {
00290 printf("\nUsage: %s [OPTION]\n", progname);
00291 printf("A caller id UDP broadcast utility for MythTV notify.\n\n");
00292 printf(" -i, --udpport_in : UDP port to monitor (--udpport_in=6947)\n");
00293 printf(" -o, --udpport_out : UDP port to monitor (--udpport_out=6948)\n");
00294 printf(" -b, --bcast : UDP broadcast address (--bcast=255.255.255.255)\n");
00295 printf(" -x, --xslfile : XSL file to use as a template (--xslfile=cid.xml)\n");
00296 printf(" -v, --verbose : some verbose debug stuff\n");
00297 printf("\nAn XSL (XSLT) file is required - it is used to transfrom the incoming\n");
00298 printf("XML to the output XML (mythnotify)\n");
00299 }
00300
00301 int main(int argc, char *argv[])
00302 {
00303 int file_arg_found = 0;
00304 static GLOBALS globals;
00305 int done = 0;
00306 int option_index = 0, c;
00307 int rcv_udp_port;
00308 int snd_udp_port;
00309 char bcast_addr[16];
00310 int bcast_addr_hex;
00311
00312 static struct option long_options[] =
00313 {
00314 {"udpport_in", optional_argument, 0, 'i'},
00315 {"udpport_out", optional_argument, 0, 'o'},
00316 {"bcast", optional_argument, 0, 'b'},
00317 {"xslfile", required_argument, 0, 'x'},
00318 {"help", no_argument, 0, 'h'},
00319 {"verbose", no_argument, 0, 'v'},
00320 {0, 0, 0, 0}
00321 };
00322
00323
00324 strcpy(bcast_addr, "255.255.255.255");
00325 ipaddr_to_int(bcast_addr, &bcast_addr_hex);
00326 rcv_udp_port = 6947;
00327 snd_udp_port = 6948;
00328 strcpy(globals.xslt_filename, "");
00329
00330 while (1)
00331 {
00332 c = getopt_long (argc, argv, "i:o:b:x:hv",
00333 long_options, &option_index);
00334 if (c == -1)
00335 break;
00336
00337 switch (c)
00338 {
00339
00340 case 'i':
00341 rcv_udp_port = atoi(optarg);
00342 break;
00343
00344 case 'o':
00345 snd_udp_port = atoi(optarg);
00346 break;
00347
00348 case 'b':
00349 strncpy(bcast_addr, optarg, sizeof(bcast_addr));
00350 if (ipaddr_to_int(bcast_addr, &bcast_addr_hex) < 0)
00351 {
00352 printf("Error in Broadcast address %s\n",
00353 bcast_addr);
00354 exit(1);
00355 }
00356 break;
00357
00358 case 'x':
00359 strncpy(globals.xslt_filename, optarg, sizeof(globals.xslt_filename)-1);
00360 file_arg_found = 1;
00361 break;
00362
00363 case 'h':
00364 print_help(argv[0]);
00365 exit(0);
00366 break;
00367
00368 case 'v':
00369 verbose = 1;
00370 printf("Verbose mode enabled\n");
00371 break;
00372
00373 default:
00374 print_help(argv[0]);
00375 exit(0);
00376 }
00377 }
00378
00379 if (!file_arg_found)
00380 {
00381 printf("No xslfile provided; --xslfile=file.xsl arg required\n");
00382 return -1;
00383 }
00384
00385 if (rcv_udp_port == snd_udp_port)
00386 {
00387 printf("UDP input and output ports cannot be the same\n");
00388 return -1;
00389 }
00390
00391 if (setup_rcv_socket(&globals.rcv_sock_handle, rcv_udp_port) < 0)
00392 {
00393 printf("error in setup_rcv_sockets\n");
00394 return -1;
00395 }
00396
00397 if (setup_snd_socket(&globals.snd_sock_handle,
00398 snd_udp_port, bcast_addr) < 0)
00399 {
00400 printf("error in setup_snd_sockets\n");
00401 return -1;
00402 }
00403
00404 done = 0;
00405 while (!done)
00406 {
00407 if (waitfor_udp(&globals, &globals.rcv_sock_handle) >= 0)
00408 {
00409
00410 if (process_udp_xml(&globals) >= 0)
00411 {
00412
00413 if (verbose)
00414 {
00415 printf("%s", globals.xml_output_buffer);
00416 }
00417
00418 send_xml_output(&globals, &globals.snd_sock_handle);
00419 }
00420 }
00421 }
00422
00423 return 0;
00424 }