/* Author : Louis-Benoit JOURDAIN (lb@jourdain.org) * based on the original work by Scott Holden ( scotth@thezone.net ) * * multi Pop3 Email checker. * * Last Updated : Feb 7th, 2002 * */ #include "Pop3Client.h" #include #include #include #include #include #include #include #include #include #include #include #include "../wmgeneral/misc.h" #include "../wmgeneral/wmgeneral.h" #include "Pop3Client.h" /* return size if all goes well, -1 if not expected answer */ int send_command(char *exp_answer, char **retour, Pop3 pc) { int size; send(pc->s, &pc->outBuf,strlen(pc->outBuf),0); size = recv(pc->s,pc->inBuf,1024,0); memset(*retour,0,1024); memcpy(*retour,pc->inBuf,size); if (strncmp(pc->inBuf, exp_answer, strlen(exp_answer))) { return (-1); } return (size); } Pop3 pop3Create(int num) { Pop3 pc; pc = (Pop3)malloc( sizeof(*pc) ); if( pc == 0) return 0; memset(pc, 0, sizeof(*pc)); pc->connected = NOT_CONNECTED; pc->serverport = 110; pc->mailCheckDelay = 10; /* make sure we perform an action first time we receive mail */ pc->forcedCheck = 1; pc->sizechanged = -1; pc->maxdlsize = -1; sprintf(pc->alias, "nb%d", num); return (pc); } int pop3MakeConnection(Pop3 pc, char *serverName, int port){ pc->s = socket(AF_INET, SOCK_STREAM, 0 ); memset( &(pc->server), 0 , sizeof(pc->server)); pc->server.sin_family = AF_INET; pc->hp = gethostbyname(serverName); if( pc->hp == 0) return -1; memcpy( &(pc->server.sin_addr), pc->hp->h_addr, pc->hp->h_length); pc->server.sin_port = htons(port); if ( connect(pc->s, (struct sockaddr *)&(pc->server) , sizeof(pc->server)) < 0 ) return -1; pc->connected = CONNECTED; return 0; } int pop3IsConnected(Pop3 pc){ if( pc->connected == CONNECTED) return 1; return 0; } int pop3Login(Pop3 pc, char *name, char *pass){ int size; char temp[1024]; if( pc->connected == NOT_CONNECTED ){ perror("Not Connected To Server\n"); return -1; } size = recv(pc->s,&pc->inBuf,1024,0); memset(temp,0,1024); memcpy(temp,pc->inBuf,size); if( temp[0] != '+' ){ fprintf(stderr,"Error Logging in\n"); return -1; } sprintf(pc->outBuf,"USER %s\r\n",name); send(pc->s, &pc->outBuf,strlen(pc->outBuf),0); size =recv(pc->s,pc->inBuf,1024,0); memset(temp,0,1024); memcpy(temp,pc->inBuf,size); if( temp[0] != '+' ){ fprintf(stderr,"Invalid User Name\n"); return -1; } memset(pc->outBuf,0,1024); sprintf(pc->outBuf,"PASS %s\r\n",pass); send(pc->s, pc->outBuf, strlen(pc->outBuf),0 ); size =recv(pc->s,&pc->inBuf,1024,0); memset(temp,0,1024); memcpy(temp,pc->inBuf,size); if( temp[0] != '+' ){ fprintf(stderr,"Incorrect Password\n"); return -1; } return 0; } int store_from(char *buf, int pos, int buflen, int num, Pop3 pc) { int end; int deb_addr; int fin_addr; int i; /* strip the white spaces */ for (; (pos < buflen) && isspace(buf[pos]); pos++); /* get the end of the line */ for (end = pos; (end + 1 < buflen) && (buf[end] != '\r') && (buf[end + 1] != '\n'); end++); /* try to find the '@' sign for the address */ for (i = pos; (i < end) && buf[i] != '@'; i++); if (i < end) { /* we found a @ sign before the end of the line */ for (deb_addr = i; deb_addr > pos && !isspace(buf[deb_addr]); deb_addr--); while (isspace(buf[deb_addr]) || ('<' == buf[deb_addr])) deb_addr++; for (fin_addr = i; fin_addr < end && !isspace(buf[fin_addr]); fin_addr++); if ('>' == buf[fin_addr - 1]) fin_addr--; memcpy(pc->mails[num].from, buf + deb_addr, (fin_addr - deb_addr > MAIL_BUFLEN) ? MAIL_BUFLEN : fin_addr - deb_addr); } else { memcpy(pc->mails[num].from, buf + pos, (end - pos > MAIL_BUFLEN) ? MAIL_BUFLEN : end - pos); } return (end + 2); } int store_subject(char *buf, int pos, int buflen, int num, Pop3 pc) { int end; int i; /* get the end of the line */ for (end = pos, i = 0; (i < MAIL_BUFLEN) && (end + 1 < buflen) && (buf[end] != '\r') && (buf[end + 1] != '\n'); end++, i++); for (; (pos < buflen) && (buf[pos] == ' '); pos++); memcpy(pc->mails[num].subject, buf + pos, end - pos); return (end + 2); } int parse_header(char *buf, int buflen, int num, Pop3 pc) { int pos = 0; int retour = 1; /* parse the header for the from and subject fields */ #ifdef _DEBUG printf(" parse_header, buflen: %d, num %d, buf:\n%s\n", buflen, num, buf); #endif while (pos < buflen) { if ((buflen > (pos + 2)) && !strncmp(".\r\n", buf + pos, 3)) { /* this is the end */ pos = buflen; retour = 0; } else if ((buflen > (pos + 4)) && !strncmp("From:", buf + pos, 5)) { pos += 5; pos = store_from(buf, pos, buflen, num, pc); } else if ((buflen > (pos + 7)) && !strncmp("Subject:", buf + pos, 8)) { pos += 8; pos = store_subject(buf, pos, buflen, num, pc); } else { for( ; (buflen > (pos + 2)) && (buf[pos] != '\r') && (buf[pos + 1] != '\n'); pos++); pos += 2; } } return (retour); } int pop3VerifStats(Pop3 pc) { int retour = 0; char *ptr; int size; if( pc->connected == NOT_CONNECTED ) return (-1); /* Find total number of messages in mail box */ sprintf(pc->outBuf,"STAT\r\n"); send(pc->s, pc->outBuf, strlen(pc->outBuf),0 ); size = recv(pc->s,pc->inBuf,1024,0); if( pc->inBuf[0] != '+' ){ perror("Error Receiving Stats"); return (-1); } ptr = strtok(pc->inBuf, " "); ptr = strtok( 0," "); ptr = strtok( 0, " "); if (ptr) retour = atoi(ptr); return (retour); } long calculate_cksum(char *buf, int len) { int i; long cumul; for (i = 0, cumul = 0; i < len; i++) { cumul += (long) buf[i]; } return (cumul); } int is_in_old_mails(long cksum, t_mail *oldmails, int numold) { int i; for (i = 0; i < numold; i++) { if (oldmails[i].cksum == cksum) return (1); } return (0); } int pop3CheckMail(Pop3 pc) { int size; char temp[4096]; char *ptr; int i; int pass; int go; int old_sizeOfAllMessages; int old_numOfMessages; int progressbarpos = 0; t_mail *oldmails; if( pc->connected == NOT_CONNECTED ) return -1; /* save the old values */ old_sizeOfAllMessages = pc->sizeOfAllMessages; old_numOfMessages = pc->numOfMessages; /* Find total number of messages in mail box */ sprintf(pc->outBuf,"STAT\r\n"); send(pc->s, pc->outBuf, strlen(pc->outBuf),0 ); size = recv(pc->s,pc->inBuf,1024,0); pc->inBuf[size] = '\0'; #ifdef _DEBUG printf(" pop3CheckMail, stat received buf (size=%d): [%s]\n", size, pc->inBuf); #endif memset(temp,0,1024); memcpy(temp,pc->inBuf,size); if( temp[0] != '+' ){ #ifdef _DEBUG printf(" Error Receiving Stats: [%s]\n", temp); #endif return (-1); } ptr = strtok(temp, " "); ptr = strtok( 0," "); pc->numOfMessages = atoi(ptr); /* save the old messages */ oldmails = pc->mails; pc->mails = NULL; /* allocate space for the new ones */ if (pc->numOfMessages) { if (NULL == (pc->mails = (t_mail *) malloc (sizeof(t_mail) * pc->numOfMessages))) { fprintf(stderr, "can't allocate memory for message structure\n"); /* clear the old messages */ if (oldmails) { free (oldmails); } return (-1); } /* Display the progress bar */ copyXPMArea(ORIG_PB_TX, ORIG_PB_TY, PROGRESSBAR_LEN, PROGRESSBAR_HEI, PROGRESSBAR_HPOS, PROGRESSBAR_VPOS); RedrawWindow(); } ptr = strtok( 0, " "); pc->sizeOfAllMessages = atoi(ptr); /* -1 means first time */ if (-1 != pc->sizechanged) pc->sizechanged = (old_sizeOfAllMessages != pc->sizeOfAllMessages); sprintf(pc->outBuf,"LAST\r\n"); send(pc->s, pc->outBuf, strlen(pc->outBuf),0 ); size = recv(pc->s,pc->inBuf,1024,0); pc->inBuf[size] = '\0'; #ifdef _DEBUG printf(" pop3CheckMail, last received buf (size=%d): [%s]\n", size, pc->inBuf); #endif memset(temp,0,1024); memcpy(temp,pc->inBuf,size); if( temp[0] != '+' ){ #ifdef _DEBUG printf(" Error Receiving LAST: [%s]\n", temp); #endif pc->numOfUnreadMessages = pc->numOfMessages; } else { ptr = strtok(temp, " "); ptr = strtok( 0," "); pc->numOfUnreadMessages = pc->numOfMessages - atoi(ptr); } /* get the subject and From fields of numOfMessages mails */ for (i = 0; i < pc->numOfMessages; i++) { #ifdef _DEBUG printf(" ---- message %d ---\n", i); #endif /* increment the progress bar by 1 message */ progressbarpos = (int) ((i + 1) * (PROGRESSBAR_LEN - 2)) / pc->numOfMessages; copyXPMArea(ORIG_PB_BARX, ORIG_PB_BARY, progressbarpos, 2, PROGRESSBAR_HPOS + 1, PROGRESSBAR_VPOS + 2); RedrawWindow(); /* reset the from and subject fields for num */ memset(&pc->mails[i], 0, sizeof(t_mail)); sprintf(pc->outBuf, "TOP %d 0\r\n", i + 1); send(pc->s, pc->outBuf, strlen(pc->outBuf), 0); pass = 0; go = 1; while (go && (0 < (size = recv(pc->s,pc->inBuf,4096,0)))) { memset(temp, 0, 4096); memcpy(temp, pc->inBuf, size); if (!pass && (temp[0] != '+')) { fprintf(stderr, "Error while getting TOP %d 0\n", i + 1); go = 0; /* must set the from and subject fields to correct value */ strncpy(pc->mails[i].from, "::error::", 9); continue; } /* calculate the checksum */ pc->mails[i].cksum += calculate_cksum(temp, size); pass++; go = parse_header(temp, size, i, pc); } pc->mails[i].todelete = 0; /* verify if this message is new or not */ pc->mails[i].new = !is_in_old_mails(pc->mails[i].cksum, oldmails, old_numOfMessages); #ifdef _DEBUG printf(" mess %d, cksum: %ld, new: %d\n", i, pc->mails[i].cksum, pc->mails[i].new); #endif } /* clear the old messages */ if (oldmails) { free (oldmails); } return 1; } int pop3WriteOneMail(int nb, int dest_fd, Pop3 pc) { int len; int retour = 0; char buf[4096]; char temp[4096]; int size; int pass = 0; int go = 1; char *deb, *fin; int theemailsize = 0; int mustdisconnect = 0; if (NOT_CONNECTED == pc->connected) { #ifdef _DEBUG printf(" pop3WriteOneMail not connected, connecting\n"); #endif if (pop3MakeConnection(pc,pc->popserver,pc->serverport) == -1){ return (1); } if (pop3Login(pc, pc->username, pc->password) == -1) { return (1); } mustdisconnect = 1; } /* check the size */ #ifdef _DEBUG printf(" pc->maxdlsize = [%d]\n", pc->maxdlsize); #endif if (-1 != pc->maxdlsize) { sprintf(pc->outBuf, "LIST %d\r\n", nb); #ifdef _DEBUG printf(" pop3WriteOneMail: %s", pc->outBuf); #endif send(pc->s, pc->outBuf, strlen(pc->outBuf), 0); size = recv(pc->s, pc->inBuf, 4096, 0); #ifdef _DEBUG printf(" received %d bytes\n", size); #endif memset(temp, 0, 4096); memcpy(temp, pc->inBuf, size); #ifdef _DEBUG printf(" pop3WriteOneMail: answer [%s]\n", temp); #endif if (temp[0] != '+') { sprintf(buf, TXT_ERROR, pc->mails[nb - 1].from, pc->mails[nb - 1].subject, pc->outBuf); write (dest_fd, buf, strlen(buf)); #ifdef _DEBUG printf(" bad answer: [%s]\n", pc->inBuf); #endif return (0); } /* go get the size of the email, strip +OK */ for (deb = temp; *deb && !isspace(*deb); deb++); /* strip the spaces */ for (; *deb && isspace(*deb); deb++); /* strip the email number */ for (; *deb && !isspace(*deb); deb++); /* strip the last spaces */ for (; *deb && isspace(*deb); deb++); /* go to the end of the size */ for (fin = deb; *fin && !isspace(*fin); fin++); if (*deb && *fin) { memcpy(buf, deb, fin - deb); buf[fin - deb] = '\0'; theemailsize = atoi(buf); #ifdef _DEBUG printf(" email retrieved size: %d\n", theemailsize); #endif if (theemailsize > pc->maxdlsize) { sprintf(buf, TXT_MESSAGETOOBIG, pc->mails[nb - 1].from, pc->mails[nb - 1].subject, theemailsize, pc->maxdlsize); write (dest_fd, buf, strlen(buf)); #ifdef _DEBUG printf(" email too big, return\n"); #endif return (0); } } /* else {} the command is screwed up, never mind, continue to download the mail */ } sprintf(pc->outBuf, "RETR %d\r\n", nb); #ifdef _DEBUG printf(" pop3WriteOneMail: %s", pc->outBuf); #endif send(pc->s, pc->outBuf, strlen(pc->outBuf), 0); while (go && (size = recv(pc->s, pc->inBuf, 4096, 0))) { #ifdef _DEBUG printf(" received %d bytes\n", size); #endif memset(temp, 0, 4096); memcpy(temp, pc->inBuf, size); deb = temp; if (!pass) { if (temp[0] != '+') { sprintf(buf, TXT_ERROR, pc->mails[nb - 1].from, pc->mails[nb - 1].subject, pc->outBuf); write (dest_fd, buf, strlen(buf)); retour = 1; go = 0; continue; } #ifdef _DEBUG printf(" first pass, skeep RETR answer ([%s])\n", deb); #endif /* skip the RETR answer, go to the end of the line; */ for (;*deb && ('\n' != *deb); deb++); deb++; } /* check if these are the final bits of the message */ fin = temp + size - 5; #ifdef _DEBUG printf(" size: %d, fin: [%s]\n", size, fin); #endif if (size >= 5) { if (!strncmp(fin, "\r\n.\r\n", 5)) { go = 0; /* don't write the final '.' into the message, skip it*/ size -= 3; #ifdef _DEBUG printf(" skeeping final '.', new size=%d\n", size); #endif } } else go = 0; #ifdef _DEBUG printf(" buffer (long: %d):\n[%s]\n", (temp + size) - deb, deb); #endif len = 0; while (deb < (temp + size)) { len = write(dest_fd, deb, (temp + size) - deb); deb += len; } #ifdef _DEBUG printf(" wrote %d bytes\n", len); #endif pass++; } if (mustdisconnect) { #ifdef _DEBUG printf(" pop3WriteOneMail disconnecting\n"); #endif pop3Quit(pc); } #ifdef _DEBUG printf(" pop3WriteOneMail, return\n"); #endif return (retour); } int pop3DeleteMail(int num, Pop3 pc) { int size; sprintf(pc->outBuf, "DELE %d\r\n", num); #ifdef _DEBUG printf(" %s\n", pc->outBuf); #endif send(pc->s, pc->outBuf, strlen(pc->outBuf), 0); size = recv(pc->s, pc->inBuf, 4096, 0); if ('+' != pc->inBuf[0]) { perror("error while deleting mail"); return (1); } return (0); } int pop3GetTotalNumberOfMessages( Pop3 pc ){ if( pc != 0 ) return pc->numOfMessages; return -1; } int pop3GetNumberOfUnreadMessages( Pop3 pc ){ if( pc != 0) return pc->numOfUnreadMessages; return -1; } int pop3Quit(Pop3 pc){ int size; #ifdef _DEBUG printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"); printf("XXXXXX pop3Quit -------- disconneting XXXXXXXXX\n"); printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"); #endif if( pc->connected == NOT_CONNECTED ) return -1; send(pc->s, "quit\r\n", 6,0 ); size =recv(pc->s,&pc->inBuf,1024,0); pc->connected = NOT_CONNECTED; if(pc->s != 0) close(pc->s); return 0; }