/* * hotwayd.c -- is a POP3-HTTPMail gateway. It will allow you to * check your HOTMAIL account from your unix box. * Created: Espeleta Tomas , 12-Apr-2001 * * My code uses libxml2 & a modified version of libghttp-1.0.9 * POP3 code is based on nupop-0.4, by Kevin Stone * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "httpmail.h" #include "hotwayd.h" #include "cache.h" #include "inet.h" #include #include "libghttp-1.0.9-mod/ghttp.h" #include "libghttp-1.0.9-mod/http_resp.h" #include "libghttp-1.0.9-mod/http_global.h" #include "libghttp-1.0.9-mod/http_req.h" #include "libghttp-1.0.9-mod/http_lazy.h" #include "commands_pop3.h" #undef AUTHORIZATION #define AUTHORIZATION 10 #define TRANSACTION 20 #define UPDATE 40 #define TERMINATE 80 #define AUTHORIZATION_TIMEOUT 15 #define TRANSACTION_TIMEOUT 30 static int state; /* current pop3 state */ static int timeout, timed_out = 0; /* Global variables */ char input[N_BUFLEN]; char username[N_BUFLEN]; int log_level = 1; /* log level of 0-2 depending how much detail you want */ char *prog; /* program name */ /* httpmail_ functions */ char *folder=NULL; /* which folder of our mailbox to work on */ const char *default_folder="inbox"; char buffer[N_BUFFER]; extern char version[]; /* version number of hotway, set in VERSION */ void usage(void); /****** MISC FUNCTIONS ******/ /* get the next command from the client * and put it in the global input buffer */ void get_input(void) { alarm(timeout); memset(input, 0, N_BUFLEN); if (fgets(input, N_BUFLEN, stdin) == NULL) { if (errno != EINTR) { timed_out = errno; alarm(0); strlcpy(input, "timeout\n", N_BUFLEN); } else { if (timed_out) { strlcpy(input, "timeout\n", N_BUFLEN); } } } alarm(0); } static void sig_alarm(int sig) { timed_out = -1; } /***** COMMAND HANDLERS ******/ void not_spoken_cmd( cmdtable *c, int argc, char *argv[]) { PSOUT("-ERR "); PSOUT(argv[0]); PSOUT(" not spoken here."); PCRLF; /* register_command(NULL, NULL, 0, NULL, NULL); */ } int stat_cmd_no= 0; void pass_cmd( cmdtable *c, int argc, char *argv[]) { extern cmdtable commands[]; register_command(NULL, NULL, 0, NULL, NULL); if (username[0] == '\0') { PSOUT("-ERR Please log in with USER first"); PCRLF; return; } if (argc < 2) { PSOUT("-ERR Missing password argument"); PCRLF; return; } #ifdef DEBUG fprintf(stderr,"attempting to login with %s\n",username); #endif switch (httpmail_authenticate_user(username, argv[1])) { case INVALIDUSER: if (log_level > 0) LOG("AUTH failed, hotmail said username %s invalid or server too busy, host %s\n", username, inet_client_name); PSOUT("-ERR Remote server too busy (or possibly userid invalid)"); PCRLF; return; case PASSWORDFAILED: if (log_level > 0) LOG("AUTH failed, hotmail said password for user %s invalid, host=%s\n", username, inet_client_name); PSOUT("-ERR Remote server said password was invalid"); PCRLF; return; case MUSTPAY: if (log_level > 0) LOG("AUTH failed, hotmail said you have to pay for access for user %s invalid, host=%s\n", username, inet_client_name); PSOUT("-ERR Hotmail said you must pay money to have WebDAV access"); PCRLF; return; case ERROR: if (log_level > 0) LOG("AUTH failed, unexpected response from hotmail"); PSOUT("-ERR Remote server returned unexpected status"); PCRLF; return; case 1: break; /* we logged in ok */ default: if (log_level > 0) LOG("AUTH failed, unexpected response in pass_cmd"); PSOUT("-ERR Unexpected response in pass_cmd"); PCRLF; return; } /* now try to open the folder we want to open */ switch(open_folder()) { case 0: if (log_level > 0) LOG("AUTH failed, couldn't find folder %s", folder); snprintf(buffer, N_BUFFER, "-ERR Unable to find folder %s on remote server", folder); PSOUT(buffer); PCRLF; return; case 1: break; /* we logged in ok */ default: if (log_level > 0) LOG("AUTH failed, unexpected response in pass_cmd"); PSOUT("-ERR Unexpected response in pass_cmd"); PCRLF; return; } /* mailbox is open-- we go to transaction state * According to RFC 1939, a POP server should be able to handle * the RETR command immideately after entering the TRANSACTION * state. For this to work, we need to do an implicit STAT on the * mail store, otherwise RETR will fail. The STAT will also produce * the needed +OK response for the pass command. */ httpmail_init(); /* run once to get cookies */ httpmail_stat( &commands[stat_cmd_no] ); /* and again to get +OK response */ state = TRANSACTION; /* log it */ if (log_level > 1) LOG(" IN user=%s host=%s messages=%ld\n", username, inet_client_name, httpmail_nemails()); } void user_cmd( cmdtable *c, int argc, char *argv[]) { username[0] = '\0'; /* reset username */ folder = (char *) realloc(folder, strlen(default_folder)+1); strlcpy(folder, default_folder, strlen(default_folder)+1); /* reset folder */ if (argc < 2) { PSOUT("-ERR Missing username argument"); PCRLF; return; } switch (validate_username(argv[1])) { case E_LOGIN_NO_DOMAIN: PFSOUT("-ERR username invalid, specify your full address, e.g. %s@hotmail.com\n", argv[1]); break; case E_LOGIN_UNRECOGNISED_DOMAIN: PFSOUT( "-ERR domain not supported (must be a hotmail/msn/lycos/spray domain) in %s\n", argv[1]); break; case E_LOGIN_NOTALLOWED: PFSOUT( "-ERR this %s server does not permit %s. Speak to your sysadmin\n", prog, argv[1]); break; case E_LOGIN_OK: PSOUT("+OK Username validated, Password required"); PCRLF; { char *domain_begin, *folder_pos; strlcpy(username,argv[1], N_BUFLEN); domain_begin = index( username, '@'); folder_pos = index(username,'/'); if (folder_pos) { /* if the folder name has spaces in it it will come up as extra * argvs */ int i; /* use folder_pos+1 so we get one past the slash */ int foldername_len = strlen(folder_pos+1); for(i=2;i The POP3-HTTPMail Gateway.", prog, version); PFSOUT(" Server on %s active.\015\012", inet_server_name); PFLUSH; while(state < UPDATE) { /* make sure we've flushed all output before waiting on client */ PFLUSH; /* set timeout according to our state */ switch (state) { case AUTHORIZATION: timeout = AUTHORIZATION_TIMEOUT; break; case TRANSACTION: timeout = TRANSACTION_TIMEOUT; break; } /* get the input from the client */ get_input(); #ifdef DEBUG if (log_level > 1) LOG("Command: %s",input); #endif /* init before continuing */ cmd_implemented = 0; /* init argc, argv */ for (argc = MAXARGC; argc; argv[--argc] = NULL); /* strip command from args -- * assumes the cmd points to a properly null terminated string * we'll strip off the crlf as well!! */ for(x = input; *x != '\0'; x++) { if (*x == ' ' || *x == '\n' || *x == '\r') { *x = '\0'; if (argv[argc] == NULL) { continue; } else { argc++; if (argc >= MAXARGC) { break; } } } else { if (argv[argc] == NULL) argv[argc] = x; } } if (!argc) { PSOUT("-ERR Null command"); PCRLF; continue; } /* cycle through jumptable */ for (c = commands; c->handler != NULL; c++) { if (!strcasecmp(c->name, argv[0])) { cmd_implemented++; if (c->states & state) { cmdtable *prev_command; if((prev_command = cached_command( c, argc, argv ))) { #ifdef DEBUG fprintf( stderr, "Command \"%s\" found in cache with timestamp %ld and %d arg(s)\n", prev_command->name, prev_command->timestamp, prev_command->argc ); #endif PSOUT( prev_command->response ); if( prev_command->response[ strlen(prev_command->response)-1 ] != 0x10 ){ PCRLF; } } else{ (c->handler)( c, argc, argv); } } else { PSOUT("-ERR That command is not available right now."); PCRLF; register_command( NULL, NULL, 0, NULL, NULL ); } } } if (!cmd_implemented) { PSOUT("-ERR Command not implemented"); PCRLF; } } PFLUSH; snprintf(input, N_BUFLEN, "user=%s host=%s", username, inet_client_name); if (timed_out) { if ((timed_out == 1) || (timed_out == 9)) { if (log_level > 0) LOG("TIMEOUT %s (%d) client hangup?\n", input, timed_out); } else { if (log_level > 0) LOG("TIMEOUT %s (%d)\n", input, timed_out); } return; } if (state == TERMINATE) { if (log_level > 1) LOG("EXIT host=%s\n", inet_client_name); return; } if (state == UPDATE) { if (log_level > 1) LOG(" OUT\n"); state = TERMINATE; } } /* Function: main * Entry point for hotwayd. Takes some command line arguments, processes them * and then calls main_loop() to wait for commands. Once main_loop() returns * we cleanup our mess and exit. */ int main(int argc, char **argv) { cmdtable *c= commands; int opt; char *prog_name; nice(20); /* give hotwayd the lowest priority in case it hangs and hogs CPU */ prog_name = strrchr(argv[0], '/'); /* remove slashes from progname */ if (prog_name && prog_name+1 != '\0') prog=strdup(prog_name+1); else prog=strdup(argv[0]); while ((opt = getopt(argc, argv, "a:hp:ru:q:l:v")) != -1) { char *access_list, *proxy, *proxy_username, *proxy_password; switch (opt) { case 'a': access_list = (char *)malloc(strlen(optarg)+1); strlcpy(access_list, optarg, strlen(optarg)+1); set_access_list(access_list); break; case 'h': /* display the usage page */ usage(); return 0; case 'l': /* setup proxy usage */ log_level = str2val(optarg, "log level", 0, 3); break; case 'p': /* setup proxy usage */ proxy = (char *) malloc (strlen(optarg)+1); strlcpy(proxy, optarg, strlen(optarg)+1); set_proxy(proxy); break; case 'r': set_readupdate_flag(1); break; case 'u': proxy_username = (char *) malloc (strlen(optarg)+1); strlcpy(proxy_username, optarg, strlen(optarg)+1); set_proxy_username(proxy_username); break; case 'q': proxy_password = (char *) malloc (strlen(optarg)+1); strlcpy(proxy_password, optarg, strlen(optarg)+1); set_proxy_password(proxy_password); break; case 'v': PFSOUT("%s v%s - http://hotwayd.sourceforge.net/\n", prog, version); return 0; default: usage(); return -1; } } /* check validity of command line arguments */ if (!proxy_sanity_check()) { usage(); return -1; } OPENLOG; inet_init(); signal(SIGALRM, sig_alarm); lazy_set(&lazy_handler); while( c && c->handler ){ if( strcmp( c->name, "stat")== 0 ){ stat_cmd_no= (int) (c- commands); } c++; } main_loop(); /* ok let's clean up our mess as much as we can :-) */ httpmail_destroy(); inboxprops_destroy(); CLOSELOG; DPRINTF("exiting.\n"); return(0); } void usage(void) { printf("Usage: %s [options and arguments]\n" " -a specify a file listing permitted HTTPMail accounts\n" " -h print usage page\n" " -l specify the logging level (0-2)\n" " -p [proxy:port] specify proxy\n" " -r set messages as read after downloading\n" " -u specify proxy username\n" " -q specify proxy password\n" " -v display version information\n" "Example: %s -p http://proxy:8080 -u dave -q proxypass\n" "Note: proxy can be specified without a username or password\n", prog, prog); }