Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtmail / libDtMail / Common / POP3Server.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: POP3Server.C /main/8 1998/11/10 17:09:21 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
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
36  *      Sun's request.
37  *
38  *      Copyright 1993, 1995, 1995 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42 /*
43  *                   Common Desktop Environment
44  *
45  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
46  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
47  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
48  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
49  *   (c) Copyright 1995 Digital Equipment Corp.
50  *   (c) Copyright 1995 Fujitsu Limited
51  *   (c) Copyright 1995 Hitachi, Ltd.
52  *                                                                   
53  *
54  *                     RESTRICTED RIGHTS LEGEND                              
55  *
56  *Use, duplication, or disclosure by the U.S. Government is subject to
57  *restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in
58  *Technical Data and Computer Software clause in DFARS 252.227-7013.  Rights
59  *for non-DOD U.S. Government Departments and Agencies are as set forth in
60  *FAR 52.227-19(c)(1,2).
61
62  *Hewlett-Packard Company, 3000 Hanover Street, Palo Alto, CA 94304 U.S.A.
63  *International Business Machines Corp., Route 100, Somers, NY 10589 U.S.A. 
64  *Sun Microsystems, Inc., 2550 Garcia Avenue, Mountain View, CA 94043 U.S.A.
65  *Novell, Inc., 190 River Road, Summit, NJ 07901 U.S.A.
66  *Digital Equipment Corp., 111 Powdermill Road, Maynard, MA 01754, U.S.A.
67  *Fujitsu Limited, 1015, Kamikodanaka Nakahara-Ku, Kawasaki 211, Japan
68  *Hitachi, Ltd., 6, Kanda Surugadai 4-Chome, Chiyoda-ku, Tokyo 101, Japan
69  */
70
71 #include <ctype.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <strings.h>
76 #include <sys/param.h>
77 #include <unistd.h>
78  
79 #include  <DtMail/DtMail.hh>
80 #include  <DtMail/DtMailError.hh>
81 #include  <DtMail/DtMailServer.hh>
82 #include  <DtMail/DtVirtArray.hh>
83 #include  <DtMail/IO.hh>
84
85 POP3Server::POP3Server(
86                 char                    *folder,
87                 DtMail::Session         *session,
88                 DtMail::MailBox         *mailbox,
89                 DtMailAppendCallback    append_mailbox_cb,
90                 void                    *append_mailbox_cb_data)
91 : DtMailServer(folder, session, mailbox,
92                append_mailbox_cb, append_mailbox_cb_data)
93 {
94     _lastretrieved = 0;
95     _uidlist_old = NULL;
96     _uidlist_current = NULL;
97     _uidlist_file = NULL;
98 }
99
100 POP3Server::~POP3Server()
101 {
102     if (_uidlist_old)
103       uidlist_destroy(_uidlist_old);
104     _uidlist_old = NULL;
105
106     if (_uidlist_current)
107       uidlist_destroy(_uidlist_current);
108     _uidlist_current = NULL;
109
110     if (_uidlist_file)
111       free(_uidlist_file);
112 }
113
114
115 //
116 // Delete a given message.
117 //
118 DTMailError_t
119 POP3Server::ptrans_delete(int msg)
120 {
121     if (_uidlist_current)
122     {
123         char    *uidliststr;
124         uidliststr = uidlist_find(_uidlist_current, msg, NULL);
125         _uidlist_current->remove(uidliststr);
126         free(uidliststr);
127     }
128     return do_transaction("DELE %d", msg);
129 }
130
131 //
132 // Request nth message.
133 //
134 DTMailError_t
135 POP3Server::ptrans_retrieve_start(int msg, int *lenp)
136 {
137     DTMailError_t       ok;
138     char                buf[DTMAS_POPBUFSIZE+1];
139     char                *cp;
140
141     ok = do_send("RETR %d", msg);
142     if (DTME_NoError != ok) return ok;
143     ok = ptrans_parse_response(buf);
144     if (DTME_NoError != ok) return ok;
145
146     // Look for "nnn octets" -- there may or may not be preceding cruft.
147     if ((cp = strstr(buf, " octets")) == (char *)NULL)
148       *lenp = 0;
149     else
150     {
151         while (--cp > buf && isdigit(*cp))
152             continue;
153         *lenp = atoi(cp);
154     }
155     return DTME_NoError;
156 }
157
158 //
159 // Apply for connection authorization.
160 //
161 DTMailError_t
162 POP3Server::ptrans_authorize(char*)
163 {
164     static char         *pname = "POP3Server::ptrans_authorize";
165     DTMailError_t       ok;
166
167     ok = do_transaction("USER %s", _username);
168     if (DTME_NoError != ok) return DTME_MailServerAccess_AuthorizationFailed;
169
170     ok = do_transaction("PASS %s", _password);
171     if (DTME_NoError != ok) return DTME_MailServerAccess_AuthorizationFailed;
172
173     // We're approved.
174     return DTME_NoError;
175 }
176
177 //
178 // Get range of messages to be fetched.
179 //
180 DTMailError_t
181 POP3Server::ptrans_fldstate_read(int *countp, int *newp)
182 {
183     static char         *pname = "POP3Server::ptrans_fldstate_read";
184     DTMailError_t       ok;
185     char                buf[DTMAS_POPBUFSIZE+1];
186
187     /* get the total message count */
188     ok = do_send("STAT");
189     if (DTME_NoError != ok) return ok;
190     ok = ptrans_parse_response(buf);
191     if (DTME_NoError != ok) return ok;
192     
193     sscanf(buf, "%d %*d", countp);
194
195     /*
196      * Newer, RFC-1725-conformant POP servers may not have the LAST command.
197      * We work as hard as possible to hide this ugliness, but it makes 
198      * counting new messages intrinsically quadratic in the worst case.
199      */
200     *newp = -1;
201     if (*countp > 0)
202     {
203         ok = do_send("LAST");
204         if (DTME_NoError != ok) return ok;
205         ok = ptrans_parse_response(buf);
206         if (DTME_NoError == ok)
207         {
208             int last = 0;
209
210             if (sscanf(buf, "%d", &last) == 0)
211               _logger.logError(DTM_FALSE, "Protocol Error getting range");
212
213             if (0 == _retrieveerrors) _lastretrieved = last;
214
215             *newp = (*countp - _lastretrieved);
216         }
217         else
218         {
219             if (NULL == _uidlist_old)
220             {
221                 _uidlist_old = uidlist_create();
222                 uidlist_read(_uidlist_old);
223             }
224
225             if (NULL == _uidlist_current)
226               _uidlist_current = uidlist_create();
227
228             /* grab the mailbox's UID list */
229             ok = do_transaction("UIDL");
230             if (DTME_NoError != ok)
231               return ok;
232             else
233             {
234                 int     curmsg;
235                 char    curuidstr[DTMAS_IDSIZE+1];
236
237
238                 *newp = 0;
239                 while (SockGets(buf, sizeof(buf), _sockfp))
240                 {
241                     if (buf[strlen(buf)-1] == '\n')
242                         buf[strlen(buf)-1] = '\0';
243                     if (buf[strlen(buf)-1] == '\r')
244                         buf[strlen(buf)-1] = '\r';
245
246                     if (_protologging)
247                       _logger.logError(DTM_FALSE, "%s< %s", proto_name(), buf);
248
249                     if (buf[0] == '.')
250                         break;
251                     else if (sscanf(buf, "%d %s", &curmsg, curuidstr) == 2)
252                     {
253                         char    *uidliststr = NULL;
254
255                         _uidlist_current->append(strdup(buf));
256
257                         uidliststr = uidlist_find(_uidlist_old, -1, curuidstr);
258                         if (uidliststr)
259                           (*newp)++;
260                     }
261                 }
262             }
263         }
264     }
265
266     return DTME_NoError;
267 }
268
269 //
270 // Capture the sizes of all messages.
271 //
272 DTMailError_t
273 POP3Server::ptrans_msgsizes(int, int *sizes)
274 {
275     static char         *pname = "POP3Server::ptrans_msgsizes";
276     char                buf[DTMAS_POPBUFSIZE+1];
277     DTMailError_t       ok;
278
279     ok = do_transaction("LIST");
280     if (DTME_NoError != ok) return ok;
281     
282     while (SockGets(buf, sizeof(buf), _sockfp))
283     {
284         int curmsg, cursize;
285
286         if (buf[strlen(buf)-1] == '\n')
287           buf[strlen(buf)-1] = '\0';
288         if (buf[strlen(buf)-1] == '\r')
289           buf[strlen(buf)-1] = '\r';
290
291         if (_protologging)
292           _logger.logError(DTM_FALSE, "%s< %s", proto_name(), buf);
293
294         if (buf[0] == '.')
295           break;
296         else if (sscanf(buf, "%d %d", &curmsg, &cursize) == 2)
297           sizes[curmsg-1] = cursize;
298         else
299           sizes[curmsg-1] = -1;
300     }
301
302     return DTME_NoError;
303 }
304
305 //
306 // Is the given message old?
307 //
308 int
309 POP3Server::ptrans_msgisold(int msg)
310 {
311     if (NULL == _uidlist_old)
312       return (msg <= _lastretrieved);
313     else
314     {
315         char    curuidstr[DTMAS_IDSIZE+1];
316         int     curmsg;
317         char    *uidliststr = NULL;
318
319         uidliststr = uidlist_find(_uidlist_current, msg, NULL);
320         if (sscanf(uidliststr, "%d %s", &curmsg, curuidstr) == 2)
321           uidliststr = uidlist_find(_uidlist_old, -1, curuidstr);
322         else
323           uidliststr = NULL;
324
325         if (NULL != uidliststr)
326           return 1;
327         else
328           return 0;
329     }
330 }
331
332 //
333 // parse command response.
334 //
335 DTMailError_t
336 POP3Server::ptrans_parse_response (char *argbuf)
337 {
338     DTMailError_t       ok;
339     char                buf[DTMAS_POPBUFSIZE+1];
340     char                *bufp;
341
342     if (SockGets(buf, sizeof(buf), _sockfp))
343     {
344         if (buf[strlen(buf)-1] == '\n')
345           buf[strlen(buf)-1] = '\0';
346         if (buf[strlen(buf)-1] == '\r')
347           buf[strlen(buf)-1] = '\r';
348
349         if (_protologging)
350           _logger.logError(DTM_FALSE, "%s< %s", proto_name(), buf);
351
352         bufp = buf;
353         if (*bufp == '+' || *bufp == '-')
354           bufp++;
355         else
356           return DTME_MailServerAccess_ProtocolViolation;
357
358         while (isalpha(*bufp)) bufp++;
359         *(bufp++) = '\0';
360
361         if (strcmp(buf,"+OK") == 0)
362         {
363             ok = DTME_NoError;
364         }
365         else if (strcmp(buf,"-ERR") == 0)
366         {
367             _logger.logError(DTM_FALSE, "Protocol Error reading response");
368             ok = DTME_MailServerAccess_Error;
369         }
370         else
371         {
372             _logger.logError(DTM_FALSE, "Protocol Violation reading response");
373             ok = DTME_MailServerAccess_ProtocolViolation;
374         }
375
376         if (argbuf != NULL)
377           strcpy(argbuf,bufp);
378     }
379     else 
380     {
381         _logger.logError(DTM_FALSE, "Socket Error reading response");
382         ok = DTME_MailServerAccess_SocketIOError;
383     }
384
385     return ok;
386 }
387
388 //
389 // Retrieve messages using POP3.
390 //
391 void
392 POP3Server::retrieve_messages(DtMailEnv &error)
393 {
394     DtMailServer::retrieve_messages(error);
395
396     uidlist_destroy(_uidlist_old);
397     _uidlist_old = _uidlist_current;
398     uidlist_write(_uidlist_old);
399     _uidlist_current = NULL;
400 }
401
402 DtVirtArray<char*>*
403 POP3Server::uidlist_create()
404 {
405     DtVirtArray<char*>  *uidlist = new DtVirtArray<char*>(25);
406     return uidlist;
407 }
408
409 void
410 POP3Server::uidlist_destroy(DtVirtArray<char*> *uidlist)
411 {
412     int         i, nuidliststr;
413     char        *uidliststr;
414
415     if (NULL == uidlist) return;
416
417     for (i=0, nuidliststr=uidlist->length(); i<nuidliststr; i++)
418     {
419         uidliststr = (*uidlist)[i];
420         if (NULL != uidliststr)
421           free(uidliststr);
422     }
423     delete uidlist;
424 }
425
426 char*
427 POP3Server::uidlist_find(
428                                 DtVirtArray<char*> *uidlist,
429                                 int msg,
430                                 char *uidstr)
431 {
432     char        *uidliststr;
433     int         i, nuidliststr;
434
435     if (msg < 0 && NULL == uidstr) return NULL;
436
437     for (i=0, nuidliststr=uidlist->length(); i<nuidliststr; i++)
438     {
439         int     curmsg;
440         char    curuidstr[DTMAS_IDSIZE+1];
441
442         uidliststr = (*uidlist)[i];
443         if (sscanf(uidliststr, "%d %s", &curmsg, curuidstr) == 2)
444         {
445             if ((msg < 0 || msg == curmsg) &&
446                 (uidstr == NULL || 0 == strcasecmp(uidstr, curuidstr)))
447               return uidliststr;
448         }
449     }
450     return NULL;
451 }
452
453 void
454 POP3Server::uidlist_read(DtVirtArray<char*> *uidlist)
455 {
456     static char *pname = "POP3Server::uidlist_read";
457     DtMailEnv   error;
458     FILE        *fp;
459
460     if (NULL == uidlist) return;
461
462     if (NULL == _uidlist_file)
463     {
464         char    *path = new char[MAXPATHLEN+1];
465         sprintf(path, "+/.%s@%s.uidlist", _folder, _servername);
466
467         _uidlist_file = _session->expandPath(error, (const char*) path);
468         if (NULL == _uidlist_file && error.isSet())
469         {
470             _logger.logError(
471                         DTM_FALSE,
472                         "%s:  Failed to find uidlist file %s\n",
473                         pname, path);
474             delete [] path;
475             return;
476         }
477         delete [] path;
478     }
479
480     if (NULL != (fp = fopen(_uidlist_file, "r")))
481     {
482         char    uidliststr[DTMAS_POPBUFSIZE+1];
483         char    curuidstr[DTMAS_IDSIZE+1];
484         int     curmsg;
485
486         while (NULL != fgets(uidliststr, DTMAS_POPBUFSIZE, fp))
487           if (sscanf(uidliststr, "%d %s", &curmsg, curuidstr) == 2)
488             uidlist->append(strdup(uidliststr));
489     }
490     fclose(fp);
491 }
492
493 void
494 POP3Server::uidlist_write(DtVirtArray<char*> *uidlist)
495 {
496     FILE        *fp;
497     char        *uidliststr;
498     int         i, nuidliststr;
499
500     if (NULL == uidlist || NULL == _uidlist_file) return;
501
502     if (NULL != (fp = fopen(_uidlist_file, "w")))
503     {
504         for (i=0, nuidliststr=uidlist->length(); i<nuidliststr; i++)
505         {
506             uidliststr = (*uidlist)[i];
507             fprintf(fp, "%s\n", uidliststr);
508         }
509     }
510     fclose(fp);
511
512 }