2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
26 * $TOG: DtMailServer.C /main/23 1999/01/29 14:46:18 mgreess $
28 * RESTRICTED CONFIDENTIAL INFORMATION:
30 * The information in this document is subject to special
31 * restrictions in a confidential disclosure agreement between
32 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
33 * document outside HP, IBM, Sun, USL, SCO, or Univel without
34 * Sun's specific written approval. This document and all copies
35 * and derivative works thereof must be returned or destroyed at
38 * Copyright 1993, 1995, 1995 Sun Microsystems, Inc. All rights reserved.
52 #include <sys/types.h>
57 #include <DtMail/DtMailError.hh>
58 #include <DtMail/DtMailServer.hh>
59 #include <DtMail/IO.hh>
61 #if defined(USE_ITIMER_REAL)
62 #define SIGNAL_TYPE SIGALRM
63 #define ITIMER_TYPE ITIMER_REAL
65 #define SIGNAL_TYPE SIGVTALRM
66 #define ITIMER_TYPE ITIMER_VIRTUAL
69 #define dtmasTAGCLR() *_transtag = '\0'; _transnum=0;
70 #define dtmasTAGGEN() (sprintf(_transtag, "a%04d", ++_transnum), _transtag)
71 #define dtmasTAGGET() (_transtag)
73 static jmp_buf restart;
75 DtMailServer::DtMailServer(
77 DtMail::Session *session,
78 DtMail::MailBox *mailbox,
79 DtMailAppendCallback append_mailbox_cb,
80 void *append_mailbox_cb_data)
87 _folder = strdup(folder);
93 _append_mailbox_cb = append_mailbox_cb;
94 _append_mailbox_cb_data
95 = append_mailbox_cb_data;
96 _protologging = get_mailrc_value(
98 DTMAS_PROPKEY_PROTOLOGGING,
100 _protologgingplus = get_mailrc_value(
102 DTMAS_PROPKEY_PROTOLOGGINGPLUS,
105 _password = get_mailrc_value(
107 DTMAS_PROPKEY_PASSWORD,
112 DTMAS_PROPKEY_REMOVEAFTERDELIVERY,
114 _retrieveold = get_mailrc_value(
116 DTMAS_PROPKEY_RETRIEVEOLD,
119 _servername = get_mailrc_value(
121 DTMAS_PROPKEY_SERVERNAME,
122 DTMAS_PROPDFLT_SERVERNAME);
128 _timeout = get_mailrc_value(
130 DTMAS_PROPKEY_TIMEOUT,
132 _username = get_mailrc_value(
134 DTMAS_PROPKEY_USERNAME,
138 DtMailServer::~DtMailServer()
140 if (_errorstring) free(_errorstring);
141 if (_folder) free(_folder);
142 if (_password) free(_password);
143 if (_servername) free(_servername);
144 if (_shroud) free(_shroud);
145 if (_username) free(_username);
149 DtMailServer::set_password(char *password)
151 if (NULL == password) return;
153 if (NULL != _password) free(_password);
155 if (NULL != password) _password = strdup(password);
159 // Read message content and append to mailbox
161 // len - Length of message.
163 #define SA_HANDLER_TYPE void (*)(int)
166 DtMailServer::ptrans_retrieve_readandappend(
170 static char *pname = "DtMailServer::ptrans_retrieve_readandappend";
171 static char *from = "From ";
172 struct sigaction action, o_action;
173 int from_done = FALSE;
178 memset((char*) &action, 0, sizeof(struct sigaction));
179 memset((char*) &o_action, 0, sizeof(struct sigaction));
180 action.sa_handler = (SA_HANDLER_TYPE) SIG_IGN;
181 sigaction(SIGINT, (const struct sigaction *) &action, &o_action);
189 if (DTMAS_MSGBUFSIZE - 1 > len - nread)
190 nbytes = (size_t) len - nread;
192 nbytes = DTMAS_MSGBUFSIZE - 1;
194 if (0 == (nbytes=SockRead(_msgbuf, 1, nbytes, _sockfp)))
198 "Failed to retrieve %d bytes.",
200 return DTME_MailServerAccess_SocketIOError;
202 _msgbuf[nbytes] = '\0';
206 if (NULL == SockGets(_msgbuf, DTMAS_MSGBUFSIZE, _sockfp))
208 _logger.logError(DTM_FALSE, "Failed to retrieve %d bytes", len);
209 return DTME_MailServerAccess_SocketIOError;
211 nbytes = strlen(_msgbuf);
216 // Delete all carriage returns
217 for (s = t = _msgbuf; *s; s++)
218 if (*s != '\r') *t++ = *s;
221 if (_protologgingplus)
224 "INFO: read %d of %d octets %d remaining\n%s< %s\n",
225 nbytes, len, len-nread, proto_name(), _msgbuf);
227 // Determine if we are done with this message.
228 if (proto_is_delimited())
230 char *s = const_cast<char *> (strrchr((const char *) _msgbuf, (int) '.'));
233 (s == _msgbuf || *(s-1) == '\n') &&
241 else if (nread >= len)
244 if (0 < (nbytes = strlen(_msgbuf)))
246 if (! from_done && strncmp(_msgbuf, from, strlen(from)))
251 clock = time(&clock);
252 sprintf(buffer, "%s %s %s",
253 from, _servername, ctime((const time_t *) &clock));
255 error, buffer, strlen(buffer),
256 _append_mailbox_cb_data);
260 _append_mailbox_cb(error, _msgbuf, nbytes, _append_mailbox_cb_data);
267 "%s: Failed to append mailbox %s: %s.\n",
268 pname, _folder, error.errnoMessage());
269 return DTME_AppendMailboxFile_Error;
273 // Message separation.
274 _append_mailbox_cb(error, "\n", 1, _append_mailbox_cb_data);
276 // Sink the file pointer.
277 sigaction(SIGINT, (const struct sigaction *) &o_action, NULL);
282 // Assemble command in printf(3) style and send to the server.
286 DtMailServer::do_send(char *fmt, ... )
288 static char *pname = "DtMailServer::do_send";
289 char *buf = new char[DTMAS_POPBUFSIZE+1];
294 if (proto_is_tagged())
297 (void) sprintf(buf, "%s ", dtmasTAGGET());
306 vsprintf(buf + strlen(buf), fmt, ap);
310 nbytes = SockWrite(buf, 1, strlen(buf), _sockfp);
311 if (nbytes != strlen(buf))
313 _logger.logError(DTM_FALSE, "Socket Error: writing '%s'", buf);
315 return DTME_MailServerAccess_SocketIOError;
321 if (_shroud && (cp = strstr(buf, _shroud)))
322 memset(cp, '*', strlen(_shroud));
323 buf[strlen(buf)-1] = '\0';
325 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), buf);
332 // Assemble command in printf(3) style, send to server, accept a response.
335 #define DTMAS_COMMAND_TERMINATOR "\n"
337 #define DTMAS_COMMAND_TERMINATOR "\r\n"
340 DtMailServer::do_transaction(char *fmt, ... )
342 static char *pname = "DtMailServer::do_transaction";
344 char *buf = new char[DTMAS_POPBUFSIZE+1];
348 if (proto_is_tagged())
351 (void) sprintf(buf, "%s ", dtmasTAGGET());
360 vsprintf(buf + strlen(buf), fmt, ap);
363 strcat(buf, DTMAS_COMMAND_TERMINATOR);
364 nbytes = SockWrite(buf, 1, strlen(buf), _sockfp);
365 if (nbytes != strlen(buf))
367 _logger.logError(DTM_FALSE, "Socket Error: writing '%s'", buf);
369 return DTME_MailServerAccess_SocketIOError;
375 if (_shroud && (cp = strstr(buf, _shroud)))
376 memset(cp, '*', strlen(_shroud));
377 buf[strlen(buf)-1] = '\0';
379 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), buf);
382 /* we presume this does its own response echoing */
383 ok = ptrans_parse_response(buf);
384 if (ok != DTME_NoError)
386 if (NULL == _errorstring)
387 _errorstring = strdup(buf);
394 // Get the specified mailrc value interpreted as an integer
397 DtMailServer::get_mailrc_value(
398 DtMail::Session *ssn,
406 string = get_mailrc_value(ssn, pfx, id, (char*) NULL);
410 value = atoi(string);
415 // Get the specified mailrc value interpreted as an string
418 DtMailServer::get_mailrc_value(
419 DtMail::Session *ssn,
423 DtMailBoolean decrypt)
426 DtMail::MailRc *mailrc = ssn->mailRc(error);
427 const char *value = NULL;
428 static char idbuf[DTMAS_IDSIZE+1];
429 char *charval = NULL;
431 DTMAS_CONCAT_MAILRC_KEY(idbuf, pfx, id);
432 mailrc->getValue(error, idbuf, &value, decrypt);
436 charval = strdup(dflt);
439 charval = strdup(value);
441 if (NULL != value) free((void*) value);
446 // Get the specified mailrc value interpreted as an string
449 DtMailServer::get_mailrc_value(
450 DtMail::Session *ssn,
456 DtMail::MailRc *mailrc = ssn->mailRc(error);
457 const char *value = NULL;
458 static char idbuf[DTMAS_IDSIZE+1];
459 Boolean boolval = FALSE;
461 DTMAS_CONCAT_MAILRC_KEY(idbuf, pfx, id);
462 mailrc->getValue(error, idbuf, &value);
468 if (NULL != value) free((void*) value);
473 // Returns TRUE if the folder being accessed is the inbox.
476 DtMailServer::is_inbox()
478 if (NULL==_folder || 0==strncmp(_folder, DTMAS_INBOX, strlen(DTMAS_INBOX)))
485 // Send message to the front end for display in the status line.
488 DtMailServer::send_info_message(DtMailCallbackOp op)
490 const char *errmsg = (const char *) _info;
492 #if defined(SEPARATE_FRONT_AND_BACK_END)
494 DtMailEventPacket event;
496 event.key = _mailbox->getObjectKey();
497 event.target = DTM_TARGET_MAILBOX;
498 event.target_object = _mailbox;
499 event.operation = (void*) op;
501 event.argument = strdup(errmsg);
503 event.argument = NULL;
504 event.event_time = time(NULL);
506 _session->writeEventData(error, &event, sizeof(event));
508 _mailbox->callCallback(op, strdup(errmsg));
513 // Retrieve messages from server using given protocol method table
516 DtMailServer::retrieve_messages(DtMailEnv &error)
518 DTMailError_t ok = DTME_NoError;
520 struct sigaction action, o_action;
521 int *msgsizes = (int*) NULL;
522 int *msgisold = (int*) NULL;
526 /* set up the server-nonresponse timeout */
527 memset((char*) &action, 0, sizeof(struct sigaction));
528 memset((char*) &o_action, 0, sizeof(struct sigaction));
529 action.sa_handler = (SA_HANDLER_TYPE) vtalarm_handler;
531 sigaction(SIGNAL_TYPE, (const struct sigaction *) &action, &o_action);
533 if ((js = setjmp(restart)) == 1)
537 "Timeout after %d seconds waiting for %s.\n",
538 _timeout, _servername);
539 ok = DTME_MailServerAccess_Error;
543 /* error message printed at point of longjmp */
544 ok = DTME_MailServerAccess_Error;
548 char buf[DTMAS_POPBUFSIZE+1];
549 int len, num, count, numnew;
553 if (proto_requires_password() && NULL == _password)
555 ok = DTME_MailServerAccess_MissingPassword;
559 vtalarm_setitimer(_timeout);
560 _info.vSetError(DTME_MailServerAccessInfo_SocketOpen,
561 DTM_FALSE, NULL, _username, _servername);
562 send_info_message(DTMC_SERVERACCESSINFO);
563 if (NULL != _errorstring)
568 _sockfp = SockOpen(_servername, proto_port(), &_errorstring);
571 if (NULL == _errorstring)
572 _errorstring = strdup(_logger.errnoMessage());
573 ok = DTME_MailServerAccess_SocketIOError;
576 else if (_protologging)
578 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), "SockOpen");
581 /* accept greeting message from mail server */
582 vtalarm_setitimer(_timeout);
583 ok = ptrans_parse_response(buf);
584 if (ok != DTME_NoError) goto closeServer;
586 /* try to get authorized to fetch mail */
587 vtalarm_setitimer(_timeout);
589 ok = ptrans_authorize(buf);
590 _shroud = (char*) NULL;
591 if (ok != DTME_NoError) goto closeServer;
593 /* compute number of messages and number of new messages waiting */
594 vtalarm_setitimer(_timeout);
595 ok = ptrans_fldstate_read(&count, &numnew);
596 if (ok != DTME_NoError) goto closeServer;
598 /* show user how many messages we downloaded */
604 "INFO: No mail from %s@%s",
605 _username, _servername);
608 if (numnew != -1 && (count - numnew) > 0)
611 "INFO: %d message%s (%d seen) from %s@%s.",
612 count, count > 1 ? "s" : "", count-numnew,
613 _username, _servername);
617 "INFO: %d message%s from %s@%s.",
618 count, count > 1 ? "s" : "",
619 _username, _servername);
628 /* we may need to get sizes in order to check message limits */
631 msgsizes = (int *)malloc(sizeof(int) * count);
633 vtalarm_setitimer(_timeout);
634 ok = ptrans_msgsizes(count, msgsizes);
635 if (ok != DTME_NoError) goto closeServer;
640 msgisold = (int*) malloc(sizeof(int) * count);
642 for (num = 1; num <= count; num++)
644 vtalarm_setitimer(_timeout);
645 msgisold[num-1] = ptrans_msgisold(num);
649 for (num = 1; num <= count; num++)
651 int toolarge = msgsizes && (msgsizes[num-1] > _sizelimit);
652 int ignoreold = msgisold && msgisold[num-1];
654 if (! (toolarge || ignoreold)) nmsgtofetch++;
659 _info.vSetError(DTME_MailServerAccessInfo_NoMessages,
660 DTM_FALSE, NULL, _username, _servername);
661 send_info_message(DTMC_SERVERACCESSINFO);
665 * In IMAP4 you can retrieve a message without marking it as
666 * having been seen while in POP3 and IMAP2BIS this is NOT
667 * possible. The proto_is_peek_capable encapsulates this
670 * The problem is when there is any kind of transient error
671 * (DNS lookup failure, or sendmail refusing delivery due to
672 * process-table limits) during retrieval, the message will
673 * be marked "seen" on the server without having been retrieved
676 * We need to keep track of any errors which take place in a
677 * given retrieval pass (_retrieveerrors). If there were any
678 * errors during the previous pass, all messages which were new
679 * at that time will be delivered.
682 /* read, forward, and delete messages */
684 for (num = 1, fetched = 0; nmsgtofetch && num <= count; num++)
686 int toolarge = msgsizes && (msgsizes[num-1] > _sizelimit);
687 int ignoreold = msgisold && msgisold[num-1];
690 * We may want to reject this message if it is
693 if (toolarge || ignoreold)
697 _logger.logError(DTM_FALSE,"skipping message %d",num);
701 " (oversized, %d bytes)",
707 DTME_MailServerAccessInfo_MessageTooLarge,
708 DTM_FALSE, NULL, msgsizes[num-1]);
709 send_info_message(DTMC_SERVERACCESSINFOERROR);
714 vtalarm_setitimer(_timeout);
717 * Fetch a message from the server.
720 _info.vSetError(DTME_MailServerAccessInfo_RetrievingMessage,
722 fetched, nmsgtofetch,
723 _username, _servername);
724 send_info_message(DTMC_SERVERACCESSINFO);
726 ok = ptrans_retrieve_start(num, &len);
727 if (ok != DTME_NoError)
736 "INFO: reading message %d (%d bytes)",
739 /* Read the message and append it to the mailbox. */
740 vtalarm_setitimer(_timeout);
741 ok = ptrans_retrieve_readandappend(error, len);
742 if (ok != DTME_NoError)
748 /* Tell the server we got it OK and resynchronize. */
749 vtalarm_setitimer(_timeout);
750 ok = ptrans_retrieve_end(num);
751 if (ok != DTME_NoError)
757 /* Mark the message seen and remove it from the server. */
758 if (_removeafterdelivery)
762 _logger.logError(DTM_FALSE, " deleted\n");
763 vtalarm_setitimer(_timeout);
764 ok = ptrans_delete(num);
765 if (ok != DTME_NoError) goto closeServer;
767 else if (_protologging)
768 _logger.logError(DTM_FALSE, " not deleted\n");
772 /* Remove all messages flagged for deletion. */
775 vtalarm_setitimer(_timeout);
776 ok = ptrans_fldstate_expunge();
777 if (ok != DTME_NoError) goto closeServer;
782 _info.vSetError(DTME_MailServerAccessInfo_NoMessages,
783 DTM_FALSE, NULL, _username, _servername);
784 send_info_message(DTMC_SERVERACCESSINFO);
788 vtalarm_setitimer(_timeout);
789 if (ok != DTME_MailServerAccess_SocketIOError)
790 if (ok == DTME_NoError)
793 (void) ptrans_quit();
794 vtalarm_setitimer(0);
802 if (ok != DTME_NoError)
804 if (NULL == _errorstring)
805 _errorstring = strdup("");
808 _username, _servername, proto_name(), _errorstring);
811 "Error while fetching from '%s': \n%s\n",
812 _servername, (const char*) error);
823 if (msgsizes != NULL) free(msgsizes);
824 if (msgisold != NULL) free(msgisold);
825 sigaction(SIGNAL_TYPE, (const struct sigaction *) &o_action, NULL);
829 // Reset the nonresponse-timeout
832 #define TV_USEC_TYPE long
834 #define TV_USEC_TYPE int
837 DtMailServer::vtalarm_setitimer(int timeout_seconds)
839 struct itimerval ntimeout;
841 ntimeout.it_interval.tv_sec = (time_t) 0;
842 ntimeout.it_interval.tv_usec = (TV_USEC_TYPE) 0;
843 ntimeout.it_value.tv_sec = (time_t) timeout_seconds;
844 ntimeout.it_value.tv_usec = (TV_USEC_TYPE) 0;
845 setitimer(ITIMER_TYPE, &ntimeout, (struct itimerval*) NULL);
849 // Handle server-timeout ALARM signal
852 DtMailServer::vtalarm_handler(int)