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 #if defined(reallyoldsun) || defined(USL)
164 #define SA_HANDLER_TYPE void (*)(void)
166 #define SA_HANDLER_TYPE void (*)(int)
170 DtMailServer::ptrans_retrieve_readandappend(
174 static char *pname = "DtMailServer::ptrans_retrieve_readandappend";
175 static char *from = "From ";
176 struct sigaction action, o_action;
177 int from_done = FALSE;
182 memset((char*) &action, 0, sizeof(struct sigaction));
183 memset((char*) &o_action, 0, sizeof(struct sigaction));
184 action.sa_handler = (SA_HANDLER_TYPE) SIG_IGN;
185 sigaction(SIGINT, (const struct sigaction *) &action, &o_action);
193 if (DTMAS_MSGBUFSIZE - 1 > len - nread)
194 nbytes = (size_t) len - nread;
196 nbytes = DTMAS_MSGBUFSIZE - 1;
198 if (0 == (nbytes=SockRead(_msgbuf, 1, nbytes, _sockfp)))
202 "Failed to retrieve %d bytes.",
204 return DTME_MailServerAccess_SocketIOError;
206 _msgbuf[nbytes] = '\0';
210 if (NULL == SockGets(_msgbuf, DTMAS_MSGBUFSIZE, _sockfp))
212 _logger.logError(DTM_FALSE, "Failed to retrieve %d bytes", len);
213 return DTME_MailServerAccess_SocketIOError;
215 nbytes = strlen(_msgbuf);
220 // Delete all carriage returns
221 for (s = t = _msgbuf; *s; s++)
222 if (*s != '\r') *t++ = *s;
225 if (_protologgingplus)
228 "INFO: read %d of %d octets %d remaining\n%s< %s\n",
229 nbytes, len, len-nread, proto_name(), _msgbuf);
231 // Determine if we are done with this message.
232 if (proto_is_delimited())
234 char *s = const_cast<char *> (strrchr((const char *) _msgbuf, (int) '.'));
237 (s == _msgbuf || *(s-1) == '\n') &&
245 else if (nread >= len)
248 if (0 < (nbytes = strlen(_msgbuf)))
250 if (! from_done && strncmp(_msgbuf, from, strlen(from)))
255 clock = time(&clock);
256 sprintf(buffer, "%s %s %s",
257 from, _servername, ctime((const time_t *) &clock));
259 error, buffer, strlen(buffer),
260 _append_mailbox_cb_data);
264 _append_mailbox_cb(error, _msgbuf, nbytes, _append_mailbox_cb_data);
271 "%s: Failed to append mailbox %s: %s.\n",
272 pname, _folder, error.errnoMessage());
273 return DTME_AppendMailboxFile_Error;
277 // Message separation.
278 _append_mailbox_cb(error, "\n", 1, _append_mailbox_cb_data);
280 // Sink the file pointer.
281 sigaction(SIGINT, (const struct sigaction *) &o_action, NULL);
286 // Assemble command in printf(3) style and send to the server.
290 DtMailServer::do_send(char *fmt, ... )
292 static char *pname = "DtMailServer::do_send";
293 char *buf = new char[DTMAS_POPBUFSIZE+1];
298 if (proto_is_tagged())
301 (void) sprintf(buf, "%s ", dtmasTAGGET());
310 vsprintf(buf + strlen(buf), fmt, ap);
314 nbytes = SockWrite(buf, 1, strlen(buf), _sockfp);
315 if (nbytes != strlen(buf))
317 _logger.logError(DTM_FALSE, "Socket Error: writing '%s'", buf);
319 return DTME_MailServerAccess_SocketIOError;
325 if (_shroud && (cp = strstr(buf, _shroud)))
326 memset(cp, '*', strlen(_shroud));
327 buf[strlen(buf)-1] = '\0';
329 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), buf);
336 // Assemble command in printf(3) style, send to server, accept a response.
339 #define DTMAS_COMMAND_TERMINATOR "\n"
341 #define DTMAS_COMMAND_TERMINATOR "\r\n"
344 DtMailServer::do_transaction(char *fmt, ... )
346 static char *pname = "DtMailServer::do_transaction";
348 char *buf = new char[DTMAS_POPBUFSIZE+1];
352 if (proto_is_tagged())
355 (void) sprintf(buf, "%s ", dtmasTAGGET());
364 vsprintf(buf + strlen(buf), fmt, ap);
367 strcat(buf, DTMAS_COMMAND_TERMINATOR);
368 nbytes = SockWrite(buf, 1, strlen(buf), _sockfp);
369 if (nbytes != strlen(buf))
371 _logger.logError(DTM_FALSE, "Socket Error: writing '%s'", buf);
373 return DTME_MailServerAccess_SocketIOError;
379 if (_shroud && (cp = strstr(buf, _shroud)))
380 memset(cp, '*', strlen(_shroud));
381 buf[strlen(buf)-1] = '\0';
383 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), buf);
386 /* we presume this does its own response echoing */
387 ok = ptrans_parse_response(buf);
388 if (ok != DTME_NoError)
390 if (NULL == _errorstring)
391 _errorstring = strdup(buf);
398 // Get the specified mailrc value interpreted as an integer
401 DtMailServer::get_mailrc_value(
402 DtMail::Session *ssn,
410 string = get_mailrc_value(ssn, pfx, id, (char*) NULL);
414 value = atoi(string);
419 // Get the specified mailrc value interpreted as an string
422 DtMailServer::get_mailrc_value(
423 DtMail::Session *ssn,
427 DtMailBoolean decrypt)
430 DtMail::MailRc *mailrc = ssn->mailRc(error);
431 const char *value = NULL;
432 static char idbuf[DTMAS_IDSIZE+1];
433 char *charval = NULL;
435 DTMAS_CONCAT_MAILRC_KEY(idbuf, pfx, id);
436 mailrc->getValue(error, idbuf, &value, decrypt);
440 charval = strdup(dflt);
443 charval = strdup(value);
445 if (NULL != value) free((void*) value);
450 // Get the specified mailrc value interpreted as an string
453 DtMailServer::get_mailrc_value(
454 DtMail::Session *ssn,
460 DtMail::MailRc *mailrc = ssn->mailRc(error);
461 const char *value = NULL;
462 static char idbuf[DTMAS_IDSIZE+1];
463 Boolean boolval = FALSE;
465 DTMAS_CONCAT_MAILRC_KEY(idbuf, pfx, id);
466 mailrc->getValue(error, idbuf, &value);
472 if (NULL != value) free((void*) value);
477 // Returns TRUE if the folder being accessed is the inbox.
480 DtMailServer::is_inbox()
482 if (NULL==_folder || 0==strncmp(_folder, DTMAS_INBOX, strlen(DTMAS_INBOX)))
489 // Send message to the front end for display in the status line.
492 DtMailServer::send_info_message(DtMailCallbackOp op)
494 const char *errmsg = (const char *) _info;
496 #if defined(SEPARATE_FRONT_AND_BACK_END)
498 DtMailEventPacket event;
500 event.key = _mailbox->getObjectKey();
501 event.target = DTM_TARGET_MAILBOX;
502 event.target_object = _mailbox;
503 event.operation = (void*) op;
505 event.argument = strdup(errmsg);
507 event.argument = NULL;
508 event.event_time = time(NULL);
510 _session->writeEventData(error, &event, sizeof(event));
512 _mailbox->callCallback(op, strdup(errmsg));
517 // Retrieve messages from server using given protocol method table
520 DtMailServer::retrieve_messages(DtMailEnv &error)
522 DTMailError_t ok = DTME_NoError;
524 struct sigaction action, o_action;
525 int *msgsizes = (int*) NULL;
526 int *msgisold = (int*) NULL;
530 /* set up the server-nonresponse timeout */
531 memset((char*) &action, 0, sizeof(struct sigaction));
532 memset((char*) &o_action, 0, sizeof(struct sigaction));
533 action.sa_handler = (SA_HANDLER_TYPE) vtalarm_handler;
535 sigaction(SIGNAL_TYPE, (const struct sigaction *) &action, &o_action);
537 if ((js = setjmp(restart)) == 1)
541 "Timeout after %d seconds waiting for %s.\n",
542 _timeout, _servername);
543 ok = DTME_MailServerAccess_Error;
547 /* error message printed at point of longjmp */
548 ok = DTME_MailServerAccess_Error;
552 char buf[DTMAS_POPBUFSIZE+1];
553 int len, num, count, numnew;
557 if (proto_requires_password() && NULL == _password)
559 ok = DTME_MailServerAccess_MissingPassword;
563 vtalarm_setitimer(_timeout);
564 _info.vSetError(DTME_MailServerAccessInfo_SocketOpen,
565 DTM_FALSE, NULL, _username, _servername);
566 send_info_message(DTMC_SERVERACCESSINFO);
567 if (NULL != _errorstring)
572 _sockfp = SockOpen(_servername, proto_port(), &_errorstring);
575 if (NULL == _errorstring)
576 _errorstring = strdup(_logger.errnoMessage());
577 ok = DTME_MailServerAccess_SocketIOError;
580 else if (_protologging)
582 _logger.logError(DTM_FALSE, "%s> %s", proto_name(), "SockOpen");
585 /* accept greeting message from mail server */
586 vtalarm_setitimer(_timeout);
587 ok = ptrans_parse_response(buf);
588 if (ok != DTME_NoError) goto closeServer;
590 /* try to get authorized to fetch mail */
591 vtalarm_setitimer(_timeout);
593 ok = ptrans_authorize(buf);
594 _shroud = (char*) NULL;
595 if (ok != DTME_NoError) goto closeServer;
597 /* compute number of messages and number of new messages waiting */
598 vtalarm_setitimer(_timeout);
599 ok = ptrans_fldstate_read(&count, &numnew);
600 if (ok != DTME_NoError) goto closeServer;
602 /* show user how many messages we downloaded */
608 "INFO: No mail from %s@%s",
609 _username, _servername);
612 if (numnew != -1 && (count - numnew) > 0)
615 "INFO: %d message%s (%d seen) from %s@%s.",
616 count, count > 1 ? "s" : "", count-numnew,
617 _username, _servername);
621 "INFO: %d message%s from %s@%s.",
622 count, count > 1 ? "s" : "",
623 _username, _servername);
632 /* we may need to get sizes in order to check message limits */
635 msgsizes = (int *)malloc(sizeof(int) * count);
637 vtalarm_setitimer(_timeout);
638 ok = ptrans_msgsizes(count, msgsizes);
639 if (ok != DTME_NoError) goto closeServer;
644 msgisold = (int*) malloc(sizeof(int) * count);
646 for (num = 1; num <= count; num++)
648 vtalarm_setitimer(_timeout);
649 msgisold[num-1] = ptrans_msgisold(num);
653 for (num = 1; num <= count; num++)
655 int toolarge = msgsizes && (msgsizes[num-1] > _sizelimit);
656 int ignoreold = msgisold && msgisold[num-1];
658 if (! (toolarge || ignoreold)) nmsgtofetch++;
663 _info.vSetError(DTME_MailServerAccessInfo_NoMessages,
664 DTM_FALSE, NULL, _username, _servername);
665 send_info_message(DTMC_SERVERACCESSINFO);
669 * In IMAP4 you can retrieve a message without marking it as
670 * having been seen while in POP3 and IMAP2BIS this is NOT
671 * possible. The proto_is_peek_capable encapsulates this
674 * The problem is when there is any kind of transient error
675 * (DNS lookup failure, or sendmail refusing delivery due to
676 * process-table limits) during retrieval, the message will
677 * be marked "seen" on the server without having been retrieved
680 * We need to keep track of any errors which take place in a
681 * given retrieval pass (_retrieveerrors). If there were any
682 * errors during the previous pass, all messages which were new
683 * at that time will be delivered.
686 /* read, forward, and delete messages */
688 for (num = 1, fetched = 0; nmsgtofetch && num <= count; num++)
690 int toolarge = msgsizes && (msgsizes[num-1] > _sizelimit);
691 int ignoreold = msgisold && msgisold[num-1];
694 * We may want to reject this message if it is
697 if (toolarge || ignoreold)
701 _logger.logError(DTM_FALSE,"skipping message %d",num);
705 " (oversized, %d bytes)",
711 DTME_MailServerAccessInfo_MessageTooLarge,
712 DTM_FALSE, NULL, msgsizes[num-1]);
713 send_info_message(DTMC_SERVERACCESSINFOERROR);
718 vtalarm_setitimer(_timeout);
721 * Fetch a message from the server.
724 _info.vSetError(DTME_MailServerAccessInfo_RetrievingMessage,
726 fetched, nmsgtofetch,
727 _username, _servername);
728 send_info_message(DTMC_SERVERACCESSINFO);
730 ok = ptrans_retrieve_start(num, &len);
731 if (ok != DTME_NoError)
740 "INFO: reading message %d (%d bytes)",
743 /* Read the message and append it to the mailbox. */
744 vtalarm_setitimer(_timeout);
745 ok = ptrans_retrieve_readandappend(error, len);
746 if (ok != DTME_NoError)
752 /* Tell the server we got it OK and resynchronize. */
753 vtalarm_setitimer(_timeout);
754 ok = ptrans_retrieve_end(num);
755 if (ok != DTME_NoError)
761 /* Mark the message seen and remove it from the server. */
762 if (_removeafterdelivery)
766 _logger.logError(DTM_FALSE, " deleted\n");
767 vtalarm_setitimer(_timeout);
768 ok = ptrans_delete(num);
769 if (ok != DTME_NoError) goto closeServer;
771 else if (_protologging)
772 _logger.logError(DTM_FALSE, " not deleted\n");
776 /* Remove all messages flagged for deletion. */
779 vtalarm_setitimer(_timeout);
780 ok = ptrans_fldstate_expunge();
781 if (ok != DTME_NoError) goto closeServer;
786 _info.vSetError(DTME_MailServerAccessInfo_NoMessages,
787 DTM_FALSE, NULL, _username, _servername);
788 send_info_message(DTMC_SERVERACCESSINFO);
792 vtalarm_setitimer(_timeout);
793 if (ok != DTME_MailServerAccess_SocketIOError)
794 if (ok == DTME_NoError)
797 (void) ptrans_quit();
798 vtalarm_setitimer(0);
806 if (ok != DTME_NoError)
808 if (NULL == _errorstring)
809 _errorstring = strdup("");
812 _username, _servername, proto_name(), _errorstring);
815 "Error while fetching from '%s': \n%s\n",
816 _servername, (const char*) error);
827 if (msgsizes != NULL) free(msgsizes);
828 if (msgisold != NULL) free(msgisold);
829 sigaction(SIGNAL_TYPE, (const struct sigaction *) &o_action, NULL);
833 // Reset the nonresponse-timeout
836 #define TV_USEC_TYPE long
838 #define TV_USEC_TYPE int
841 DtMailServer::vtalarm_setitimer(int timeout_seconds)
843 struct itimerval ntimeout;
845 ntimeout.it_interval.tv_sec = (time_t) 0;
846 ntimeout.it_interval.tv_usec = (TV_USEC_TYPE) 0;
847 ntimeout.it_value.tv_sec = (time_t) timeout_seconds;
848 ntimeout.it_value.tv_usec = (TV_USEC_TYPE) 0;
849 setitimer(ITIMER_TYPE, &ntimeout, (struct itimerval*) NULL);
853 // Handle server-timeout ALARM signal
856 DtMailServer::vtalarm_handler(int)