#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
//#include <iconv.h>
#define LOCKFILE "/var/run/bbs4gzhu.pid"
#define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int lockfile; /* 锁文件的描述字 */
int exit_flag = 0;
#define BUFFER_SIZE 409600
#define FORMHASH_SIZE 16
#define USERNAME_SIZE 128
#define PASSWORD_SIZE 128
#define SID_SIZE 64
#define AUTH_SIZE 128
int background = 0;
int interval = 15;
char *cookiepath = "";
char username[USERNAME_SIZE] = "";
char password[PASSWORD_SIZE] = "";
char *host = "bbs.gzhu.edu.cn";
short port = 80;
static void
flock_reg ()
{
char buf[16];
struct flock fl;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_type = F_WRLCK;
fl.l_pid = getpid();
//阻塞式的加锁
if (fcntl (lockfile, F_SETLKW, &fl) < 0){
perror ("fcntl_reg");
exit(EXIT_FAILURE);
}
//把pid写入锁文件
ftruncate (lockfile, 0);
sprintf (buf, "%ld", (long)getpid());
write (lockfile, buf, strlen(buf) + 1);
}
void
daemon_init(void)
{
pid_t pid;
int fd0;
if ( (pid = fork()) < 0)
perror ("Fork");
else if (pid != 0) {
fprintf(stdout, "bbs4gzhu&&Info: Forked background with PID: [%d]\n\n", pid);
exit(EXIT_SUCCESS);
}
setsid(); /* become session leader */
chdir("/tmp"); /* change working directory */
umask(0); /* clear our file mode creation mask */
flock_reg ();
fd0 = open ("/dev/null", O_RDWR);
dup2 (fd0, STDIN_FILENO);
dup2 (fd0, STDERR_FILENO);
dup2 (fd0, STDOUT_FILENO);
close (fd0);
}
static int
is_running()
{
struct flock fl;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_type = F_WRLCK;
//尝试获得文件锁
if (fcntl (lockfile, F_GETLK, &fl) < 0){
perror ("fcntl_get");
exit(EXIT_FAILURE);
}
if (exit_flag) {
if (fl.l_type != F_UNLCK) {
if ( kill (fl.l_pid, SIGINT) == -1 )
perror("kill");
fprintf (stdout, "bbs4gzhu&&Info: Kill Signal Sent to PID %d.\n", fl.l_pid);
}
else
fprintf (stderr, "bbs4gzhu&&Info: Program not running.\n");
exit (EXIT_FAILURE);
}
//没有锁,则给文件加锁,否则返回锁着文件的进程pid
if (fl.l_type == F_UNLCK) {
flock_reg ();
return 0;
}
return fl.l_pid;
}
static void
signal_interrupted (int signo)
{
exit (EXIT_SUCCESS);
exit_flag = 1;
fprintf(stdout,"\nbbs4gzhu&&Info: Interrupted. \n");
}
static void show_usage()
{
printf("auto online for bbs.gzhu.edu.cn \n\
options: \n \
-b --background. run in background. mostly this is for service mode. \n\
-e --exit. quit the running program in background \n\
-i --interval value(in second). interval time between online request. default value is 15 seconds. \n\
-h --help. show this usage message. \n\
-c --cookiepath. specifies the file containing the cookies for online request. \n\
-u --username. username for login in the bbs. \n\
-p --password. md5 of the password, used for login in the bbs. \n\
--host. host for auto online. default value is \"bbs.gzhu.edu.cn\" \n\
--port. the port with site. default value is 80. \n");
}
static void
init_arguments(int *argc, char ***argv)
{
/* Option struct for progrm run arguments */
static struct option long_options[] =
{
{"background", no_argument, &background, 'b'},
{"exit", no_argument, &exit_flag, 'e'},
{"interval", required_argument, &interval, 'i'},
{"help", no_argument, 0, 'h'},
{"cookiepath", required_argument, NULL, 'c'},
{"username", required_argument, NULL, 'u'},
{"password", required_argument, NULL, 'p'},
{"host", required_argument, NULL, 2},
{"port", required_argument, NULL, 1},
{0, 0, 0, 0}
};
int c;
while (1) {
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long ((*argc), (*argv), "bei:hc:u:p:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
break;
case 'b':
background = 1;
break;
case 'e':
exit_flag = 1;
break;
case 'i':
interval = atoi(optarg);
break;
case 'h':
show_usage();
exit(EXIT_SUCCESS);
break;
case 'c':
cookiepath = optarg;
break;
case 'u':
strncpy(username, optarg, USERNAME_SIZE);
break;
case 'p':
strncpy(password, optarg, PASSWORD_SIZE);
break;
case 2:
host = optarg;
break;
case 1:
port = atoi(optarg);
break;
case '?':
exit(EXIT_FAILURE);
break;
default:
fprintf (stderr,"Unknown option character `\\x%x'.\n", c);
exit(EXIT_FAILURE);
}
}
}
void PANIC(char *msg);
#define PANIC(msg) {perror(msg); abort();}
int getitem(const char *str, char *buffer, const int size, const char *start, const char end);
int tcp_connect(const char *host, const short port);
struct sockaddr_in getaddr(const char *host, const short port);
void loadcookies(const char *filename, char *buffer, const int size);
int
code_convert(char *from_charset, char *to_charset,
char *inbuf, size_t inlen, char *outbuf, size_t outlen);
int parse_username(const char *str, char *buffer, const int size);
int parse_formhash(const char *str, char *buffer, const int size);
int parse_sid(const char *str, char *buffer, const int size);
int parse_auth(const char *str, char *buffer, const int size);
int parse_parse_cookies(const char *str, char *buffer, const int size);
int parse_welcome_msg(const char *str, char *buffer, const int size);
int parse_return_msg(const char *str, char *buffer, const int size);
int parse_error_msg(const char *str, char *buffer, const int size);
int login(const char *username, const char *password, char *cookies, const int size);
int send_request(const int sockfd, const char *url, const char *header, const char *data, char *buffer, const int size);
int check_http(const char *response, const int size);
int online(const char *cookies);
char *gettime();
int
main(int argc, char *argv[])
{
int ins_pid;
char cookies[BUFFER_SIZE];
init_arguments(&argc, &argv);
lockfile = open (LOCKFILE, O_RDWR | O_CREAT , LOCKMODE);
if (lockfile < 0){
perror ("Lockfile");
exit(EXIT_FAILURE);
}
if ( (ins_pid = is_running()) ) {
fprintf(stderr,"bbs4gzhu@@ERROR: Program already "
"running with PID %d\n", ins_pid);
exit(EXIT_SUCCESS);
}
signal (SIGINT, signal_interrupted);
signal (SIGTERM, signal_interrupted);
bzero(cookies, sizeof(cookies));
if(strlen(cookiepath) > 0) {
loadcookies(cookiepath, cookies, sizeof(cookies));
if(strlen(username) <= 0) {
if(parse_username(cookies, username, sizeof(username)) <= 0) {
if(parse_sid(cookies, username, sizeof(username)) <= 0) {
strcpy(username, "unknown");
}
}
}
} else {
if(strlen(username) <= 0) {
strcpy(username, "nobody");
}
}
printf("current username: %s \n", username);
if(background) daemon_init();
while(1) {
printf("%s logging...", gettime());
fflush(stdout);
if(!login(username, password, cookies, sizeof(cookies))) {
sleep(interval);
continue;
}
printf("\n");
while(online(cookies)) {
printf("%s online...\r", gettime()); fflush(stdout);
sleep(interval);
}
printf("%s offline...it will auto retrying!\n", gettime());
}
return 0;
}/* ---------- end of function main ---------- */
char *gettime()
{
static char str[64];
struct tm *local;
time_t t;
t = time(NULL);
local=localtime(&t);
sprintf(str, "%02d:%02d:%02d", local->tm_hour, local->tm_min, local->tm_sec);
return str;
}
int check_http(const char *response, const int size)
{
char buffer[16];
if(size <= 16) return 0;
if(strstr(response, "HTTP/1.1 200 OK\r\n") == NULL) {
strncpy(buffer, response, 15);
buffer[15] = 0;
printf("%s bad status:%s\n", username, buffer);
return 0;
}
return 1;
}
void loadcookies(const char *filename, char *buffer, const int size)
{
int bytes_read;
FILE *fd = fopen(filename, "r");
if(fd == NULL) {
PANIC("loadcookies");
}
bytes_read = fread(buffer, 1, size, fd);
buffer[bytes_read - 1] = 0;
while(bytes_read--) {
if(buffer[bytes_read] == '\r') buffer[bytes_read] = ' ';
if(buffer[bytes_read] == '\n') buffer[bytes_read] = ' ';
}
fclose(fd);
}
int send_request(const int sockfd, const char *url, const char *header, const char *data, char *buffer, const int size)
{
int bytes_read = 0;
char request_buffer[BUFFER_SIZE];
if(data != NULL) {
sprintf(request_buffer, "POST %s HTTP/1.1\r\nHost: %s:%d\r\nReferer: http://%s\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %d\r\n%s\r\n\r\n%s", url, host, port, host, strlen(data), header, data);
} else {
sprintf(request_buffer, "GET %s HTTP/1.1\r\nHost: %s:%d\r\nReferer: http://%s\r\n%s\r\n", url, host, port, host, header);
}
//printf("%s\n", request_buffer);
/* 发送数据 */
send(sockfd, request_buffer, strlen(request_buffer), 0);
/* 接收缓存 */
bzero(buffer, sizeof(size));
/* 接收 */
bytes_read = recv(sockfd, buffer, size, 0);
//printf("%s\n", buffer);
return bytes_read;
}
int login(const char *username, const char *password, char *cookies, const int size)
{
int sockfd;
int bytes_read;
char buffer[BUFFER_SIZE];
char header[BUFFER_SIZE];
char formhash[FORMHASH_SIZE];
char formdata[BUFFER_SIZE];
char sid[SID_SIZE];
char auth[AUTH_SIZE];
char msg[BUFFER_SIZE];
/* 先获取登录页面 */
sockfd = tcp_connect(host, port);
bytes_read = send_request(sockfd, "/logging.php?action=login&inajax=1", "", NULL, buffer, sizeof(buffer));
close(sockfd);
if(check_http(buffer, bytes_read) == 0) return 0;
// printf("%s\n", buffer);
/* 若被列入黑名单,则等待3分钟后再试 */
if(strlen(strstr(buffer, "\r\n\r\n")+4) <= 120) {//这个值一般在99左右。
bytes_read = parse_error_msg(buffer, msg, sizeof(msg));
if(bytes_read > 0 && strstr(msg, "\n") == NULL) {
printf("Error:%s . Try 15m later..\r", msg);
} else {
printf("Error:unknown. Try 3 minute later..\r", msg);
}
fflush(stdout);
sleep(3*60);
return 0;
}
/* 解析formhash */
bytes_read = parse_formhash(buffer, formhash, sizeof(formhash));
if(bytes_read <= 0) return 0;
/* 解析SID */
bytes_read = parse_sid(buffer, sid, sizeof(sid));
if(bytes_read <= 0) return 0;
strcat(cookies, "hLR_sid="); strcat(cookies, sid); strcat(cookies, ";");
/* 构造header */
sprintf(header, "Cookie: %s\r\n", cookies);
/* 构造登录表单 */
sprintf(formdata, "formhash=%s&loginfield=username&username=%s&password=%s&questionid=0&answer=&cookietime=2592000", formhash, username, password);
// printf("%s %s\n", header, formdata);
/* 提交登录请求 */
sockfd = tcp_connect(host, port);
bytes_read = send_request(sockfd, "/logging.php?action=login&loginsubmit=yes&inajax=1", header, formdata, buffer, sizeof(buffer));
close(sockfd);
if(check_http(buffer, bytes_read) == 0) return 0;
/* 解析欢迎消息 */
bytes_read = parse_welcome_msg(buffer, msg, sizeof(msg));
if(bytes_read <= 0) {
/* 解析登录返回信息 */
bytes_read = parse_return_msg(buffer, msg, sizeof(msg));
if(bytes_read > 0) printf("Failed Info:%s\r", msg);
return 0;
}
printf("Login Info:%s\r", msg);
fflush(stdout);
/* 解析auth */
bytes_read = parse_auth(buffer, auth, sizeof(auth));
if(bytes_read <= 0) return 0;
strcat(cookies, "hLR_auth="); strcat(cookies, auth); strcat(cookies, ";");
/* 有效时间 */
strcat(cookies, "hLR_cookietime=2592000;");
return strlen(cookies);
}
int online(const char *cookies)
{
int sockfd;
int bytes_read;
char buffer[BUFFER_SIZE];
char header[BUFFER_SIZE];
/* 构造header */
sprintf(header, "Cookie: %s\r\n", cookies);
/* 刷新页面保持在线 */
sockfd = tcp_connect(host, port);
bytes_read = send_request(sockfd, "/search.php", header, NULL, buffer, sizeof(buffer));
close(sockfd);
if(check_http(buffer, bytes_read) == 0) return 0;
/* 检测用户凭据是否到期 */
if(strstr(buffer, "logging.php?action=logout") == NULL) return 0;
return 1;
}
int parse_error_msg(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "![CDATA[", ']');
}
int parse_username(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "uchome_loginuser=", ';');
}
int parse_formhash(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "name=\"formhash\" value=\"", '\"');
}
int parse_sid(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "hLR_sid=", ';');
}
int parse_auth(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "hLR_auth=", ';');
}
int parse_cookies(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "Set-Cookie: ", ';');
}
int parse_welcome_msg(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "$('messageleft').innerHTML = '", '\'');
}
int parse_return_msg(const char *str, char *buffer, const int size)
{
return getitem(str, buffer, size, "<em id=\"returnmessage\">", '<');
}
int getitem(const char *str, char *buffer, const int size, const char *start, const char end)
{
int i;
char *p = strstr(str, start);
if(p == NULL) return 0;
for(i = 0, p+= strlen(start); p[i] && i < (size - 1); i++) {
if(p[i] == end) break;
buffer[i] = p[i];
}
buffer[i] = '\0';
return i;
}
struct sockaddr_in getaddr(const char *host, const short port)
{
struct sockaddr_in sin;
struct hostent *hosts = NULL;
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if(inet_addr(host) == INADDR_NONE) {
hosts = gethostbyname(host);
if(hosts != NULL) {
sin.sin_addr = *((struct in_addr*)hosts->h_addr);
}
} else {
sin.sin_addr.s_addr = inet_addr(host);
}
return sin;
}
int
tcp_connect(const char *host, const short port)
{
int sockfd;
struct sockaddr_in endpoint;
endpoint = getaddr(host, port);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return 0;
if (connect(sockfd, (struct sockaddr*)&endpoint, sizeof(struct sockaddr_in)) != 0) {
close(sockfd);
return 0;
}
return (sockfd);
}
/*
* === FUNCTION ======================================================================
* Name: code_convert
* Description: 字符串编码转换
* =====================================================================================
*/
int
code_convert(char *from_charset, char *to_charset,
char *inbuf, size_t inlen, char *outbuf, size_t outlen)
{
memcpy(outbuf, inbuf, inlen < outlen ? inlen : outlen);
/*
iconv_t cd;
cd = iconv_open(to_charset,from_charset);
if (cd==0)
return -1;
memset(outbuf,0,outlen);
if (iconv (cd, &inbuf, &inlen, &outbuf, &outlen)==-1)
return -1;
iconv_close(cd);
*/
return 0;
}