Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
464 lines (406 sloc) 10.3 KB
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libnotify/notify.h>
#include <libxml/parser.h>
#include "na.h"
#include "http.h"
#include "config.h"
static const char na_auth1_uri[] =
"https://secure.nicovideo.jp/secure/login?site=nicolive_antenna";
static const char na_auth2_uri[] =
"http://live.nicovideo.jp/api/getalertstatus";
static const char na_info_uri[] =
"http://live.nicovideo.jp/api/getstreaminfo";
static const char config_user_default[] = ".na";
int debug;
static void display_help(void)
{
fprintf(stdout, "na - Nico nico live Alert\n"
"Usage:\n"
" na [options]\n"
"options are:\n"
" --debug\n"
" --mail mail-address\n"
" --pass password\n"
" --config configfile\n"
" --help\n");
}
static void notify(const char *title, const char *body, const char *icon)
{
NotifyNotification *n = NULL;
if (!title)
return;
notify_init("na");
n = notify_notification_new(title, body, icon);
notify_notification_set_timeout(n, NOTIFY_EXPIRES_NEVER);
notify_notification_show(n, NULL);
}
static void open_live(struct session *session, const char *uri)
{
pid_t pid;
char cmd[1024];
if (!uri)
return;
pid = fork();
/* parent */
if (pid)
return;
/* child */
snprintf(cmd, sizeof(cmd), "%s %s", session->browser, uri);
system(cmd);
exit(0);
}
static struct session *na_session_alloc(void)
{
struct session *session;
session = zalloc(sizeof(struct session));
if (!session)
return NULL;
return session;
}
static void na_session_free(struct session *session)
{
struct community *p;
if (!session)
return;
free(session->config_file);
free(session->icon);
free(session->mail);
free(session->pass);
free(session->browser);
free(session->ticket);
while (session->commu_list) {
p = session->commu_list->next;
free(session->commu_list);
session->commu_list = p;
}
free(session->cs_addr);
free(session->cs_port);
free(session->thread);
free(session);
}
static char *na_get_auth_ticket(struct session *session)
{
char *response;
char data[1024];
xmlDocPtr doc;
xmlNodePtr current;
xmlChar *ticket;
if (!session || !session->mail || !session->pass)
return NULL;
snprintf(data, sizeof(data), "mail=%s&password=%s", session->mail,
session->pass);
response = send_request(na_auth1_uri, data);
if (!response)
return NULL;
doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
XML_PARSE_NOERROR);
if (!doc)
goto fail_response;
current = xmlDocGetRootElement(doc);
if (!current)
goto fail_doc;
current = current->xmlChildrenNode;
while (current != NULL) {
if (!xmlStrcmp(current->name, (const xmlChar *)"ticket")) {
ticket = xmlNodeListGetString(doc,
current->xmlChildrenNode, 1);
session->ticket = strdup((char *)ticket);
xmlFree(ticket);
break;
}
current = current->next;
}
fail_doc:
xmlFreeDoc(doc);
fail_response:
free(response);
return session->ticket;
}
static void na_get_alert_status(struct session *session)
{
char *response;
char uri[1024];
struct community *p = NULL;
xmlDocPtr doc;
xmlNodePtr current, child;
xmlChar *co_id, *addr, *port, *thread;
if (!session || !session->ticket)
return;
snprintf(uri, sizeof(uri), "%s?ticket=%s", na_auth2_uri,
session->ticket);
response = send_request(uri, NULL);
if (!response)
return;
doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
XML_PARSE_NOERROR);
if (!doc)
goto fail_response;
current = xmlDocGetRootElement(doc);
if (!current)
goto fail_doc;
current = current->xmlChildrenNode;
while (current != NULL) {
if (!xmlStrcmp(current->name, (const xmlChar *)"communities")) {
child = current->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name,
(const xmlChar *)"community_id")) {
co_id = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
if (session->commu_list)
p = session->commu_list;
session->commu_list = zalloc(
sizeof(struct community));
session->commu_list->next = p;
session->commu_list->comu_id =
strdup((char *)co_id);
xmlFree(co_id);
}
child = child->next;
}
} else if (!xmlStrcmp(current->name, (const xmlChar *)"ms")) {
child = current->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name,
(const xmlChar *)"addr")) {
addr = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
session->cs_addr = strdup((char *)addr);
xmlFree(addr);
} else if (!xmlStrcmp(child->name,
(const xmlChar *)"port")) {
port = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
session->cs_port = strdup((char *)port);
xmlFree(port);
} else if (!xmlStrcmp(child->name,
(const xmlChar *)"thread")) {
thread = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
session->thread =
strdup((char *)thread);
xmlFree(thread);
}
child = child->next;
}
}
current = current->next;
}
fail_doc:
xmlFreeDoc(doc);
fail_response:
free(response);
}
static void na_get_stream_info(struct session *session, const char *live_id)
{
char *response;
char uri[1024], msg[1024];
time_t t;
xmlDocPtr doc;
xmlNodePtr current, child;
xmlChar *req_id, *title, *name;
if (!live_id)
return;
snprintf(uri, sizeof(uri), "%s/lv%s", na_info_uri, live_id);
response = send_request(uri, NULL);
if (!response)
return;
doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
XML_PARSE_NOERROR);
if (!doc)
goto fail_response;
current = xmlDocGetRootElement(doc);
if (!current)
goto fail_doc;
current = current->xmlChildrenNode;
while (current != NULL) {
if (!xmlStrcmp(current->name, (const xmlChar *)"request_id"))
req_id = xmlNodeListGetString(doc,
current->xmlChildrenNode, 1);
else if (!xmlStrcmp(current->name,
(const xmlChar *)"streaminfo")) {
child = current->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name,
(const xmlChar *)"title"))
title = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
child = child->next;
}
} else if (!xmlStrcmp(current->name,
(const xmlChar *)"communityinfo")) {
child = current->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name,
(const xmlChar *)"name"))
name = xmlNodeListGetString(doc,
child->xmlChildrenNode, 1);
child = child->next;
}
}
current = current->next;
}
snprintf(uri, sizeof(uri), "http://nico.ms/%s", req_id);
if (session->browser)
open_live(session, uri);
time(&t);
snprintf(msg, sizeof(msg), "%s\n【生放送】%s を開始しました。\n%s",
ctime(&t), title, uri);
notify((char *)name, msg, session->icon);
xmlFree(req_id);
xmlFree(title);
xmlFree(name);
fail_doc:
xmlFreeDoc(doc);
fail_response:
free(response);
}
static void na_handle_response(struct session *session, const char *response)
{
const char *p;
char live_id[1024], commu_id[1024];
int i;
struct community *commu_p;
if (!response)
return;
/* (Ex.)Response format (one line: null terminated):
* <chat thread="xxxxxxxxxx" no="xxxxxxxx" date="xxxxxxxxxx"
* user_id="xxx" premium="x">
* xxxxxxxx,coxxxxxxx,xxxxxxxx
* </chat>
*/
p = response;
if (strncmp(p, "<chat", strlen("<chat")))
return;
while (*p++ != '>')
;
/* Find live id */
for (i = 0; *p != ','; p++, i++)
live_id[i] = *p;
live_id[i] = '\0';
/* Find community id */
for (p++, i = 0; *p != ','; p++, i++)
commu_id[i] = *p;
commu_id[i] = '\0';
if (!session->commu_list)
return;
commu_p = session->commu_list;
for ( ; commu_p != NULL; commu_p = commu_p->next)
if (!strcmp(commu_id, commu_p->comu_id)) {
na_get_stream_info(session, live_id);
break;
}
}
static void na_loop(struct session *session)
{
int sock, ret;
struct sockaddr_in servaddr;
struct hostent *hptr;
char buf[4096];
if (!session || !session->cs_port || !session->cs_addr ||
!session->thread)
return;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
return;
bzero(&servaddr, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(session->cs_port));
hptr = gethostbyname(session->cs_addr);
if (!hptr)
return;
bcopy(hptr->h_addr, &servaddr.sin_addr, hptr->h_length);
ret = connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (ret == -1)
return;
snprintf(buf, sizeof(buf), "<thread thread='%s' version='20061206' "
"res_from='-1'/>", session->thread);
write(sock, buf, strlen(buf)+1);
while (1) {
read(sock, buf, sizeof(buf));
na_handle_response(session, buf);
}
}
int main(int argc, char **argv)
{
static const struct option options[] = {
{ "debug", 0, NULL, 'd' },
{ "help", 0, NULL, 'h' },
{ "mail", 1, NULL, 'm' },
{ "pass", 1, NULL, 'p' },
{ "config", 1, NULL, 'c' },
{ }
};
struct session *session;
int option;
int retval = 0;
char *homedir;
debug = 0;
homedir = getenv("HOME");
if (!homedir) {
fprintf(stderr, "where you live?\n");
return -1;
}
session = na_session_alloc();
if (!session) {
fprintf(stderr, "No more memory...\n");
return -1;
}
session->config_file = zalloc(strlen(homedir) + strlen("/.nicoalert/")
+ strlen(config_user_default) + 1);
sprintf(session->config_file, "%s/.nicoalert/%s", homedir,
config_user_default);
session->icon = zalloc(strlen(homedir) + strlen("/.nicoalert/") +
strlen("nico.png") + 1);
sprintf(session->icon, "%s/.nicoalert/nico.png", homedir);
parse_config(session);
while (1) {
option = getopt_long(argc, argv, "dhm:p:c:", options, NULL);
if (option == -1)
break;
switch (option) {
case 'd':
debug = 1;
break;
case 'h':
display_help();
goto exit;
case 'm':
if (session->mail)
free(session->mail);
session->mail = strdup(optarg);
break;
case 'p':
if (session->pass)
free(session->pass);
session->pass = strdup(optarg);
break;
case 'c':
if (session->config_file)
free(session->config_file);
session->config_file = strdup(optarg);
parse_config(session);
break;
default:
display_help();
goto exit;
}
}
na_get_auth_ticket(session);
na_get_alert_status(session);
daemon(-1, 0);
na_loop(session);
exit:
na_session_free(session);
return retval;
}