1 //=============================================================================
2 // File:       pop.cpp
3 // Contents:   Definitions for DwPopClient
4 // Maintainer: Doug Sauder <dwsauder@fwb.gulf.net>
5 // WWW:        http://www.fwb.gulf.net/~dwsauder/mimepp.html
6 // $Revision: 1.7 $
7 // $Date: 1998/10/31 12:33:46 $
8 //
9 // Copyright (c) 1996, 1997 Douglas W. Sauder
10 // All rights reserved.
11 //
12 // IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT,
13 // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
14 // THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER
15 // HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 //
17 // DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
18 // NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 // PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
20 // BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
21 // SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
22 //
23 //=============================================================================
24 
25 #define DW_IMPLEMENTATION
26 
27 #include <mimelib/config.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <mimelib/pop.h>
32 
33 #define POP_PORT 110
34 #define RECV_BUFFER_SIZE  8192
35 #define SEND_BUFFER_SIZE  1024
36 
37 #if defined(DW_DEBUG_POP)
38 #  define DBG_POP_STMT(x) x
39 #else
40 #  define DBG_POP_STMT(x)
41 #endif
42 
43 
DwPopClient()44 DwPopClient::DwPopClient()
45 {
46     mSendBuffer = new char[SEND_BUFFER_SIZE];
47     mRecvBuffer = new char[RECV_BUFFER_SIZE];
48     mNumRecvBufferChars = 0;
49     mRecvBufferPos = 0;
50     mStatusCode = 0;
51     mObserver = 0;
52 }
53 
54 
~DwPopClient()55 DwPopClient::~DwPopClient()
56 {
57     if (mRecvBuffer) {
58         delete [] mRecvBuffer;
59         mRecvBuffer = 0;
60     }
61     if (mSendBuffer) {
62         delete [] mSendBuffer;
63         mSendBuffer = 0;
64     }
65 }
66 
67 
Open(const char * aServer,DwUint16 aPort)68 int DwPopClient::Open(const char* aServer, DwUint16 aPort)
69 {
70     mStatusCode = 0;
71     mSingleLineResponse = mMultiLineResponse = "";
72     int err = DwProtocolClient::Open(aServer, aPort);
73     if (! err) {
74         PGetSingleLineResponse();
75     }
76     return mStatusCode;
77 }
78 
79 
SetObserver(DwObserver * aObserver)80 DwObserver* DwPopClient::SetObserver(DwObserver* aObserver)
81 {
82     DwObserver* obs = mObserver;
83     mObserver = aObserver;
84     return obs;
85 }
86 
87 
StatusCode() const88 int DwPopClient::StatusCode() const
89 {
90     return mStatusCode;
91 }
92 
93 
SingleLineResponse() const94 const DwString& DwPopClient::SingleLineResponse() const
95 {
96     return mSingleLineResponse;
97 }
98 
99 
MultiLineResponse() const100 const DwString& DwPopClient::MultiLineResponse() const
101 {
102     return mMultiLineResponse;
103 }
104 
105 
User(const char * aName)106 int DwPopClient::User(const char* aName)
107 {
108     mStatusCode = 0;
109     mSingleLineResponse = mMultiLineResponse = "";
110     mLastCommand = kCmdUser;
111     strcpy(mSendBuffer, "USER ");
112     strncat(mSendBuffer, aName, SEND_BUFFER_SIZE-32);
113     strcat(mSendBuffer, "\r\n");
114     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
115     int bufferLen = strlen(mSendBuffer);
116     int numSent = PSend(mSendBuffer, bufferLen);
117     if (numSent == bufferLen) {
118         PGetSingleLineResponse();
119     }
120     return mStatusCode;
121 }
122 
123 
Pass(const char * aPasswd)124 int DwPopClient::Pass(const char* aPasswd)
125 {
126     mStatusCode = 0;
127     mSingleLineResponse = mMultiLineResponse = "";
128     mLastCommand = kCmdPass;
129     strcpy(mSendBuffer, "PASS ");
130     strncat(mSendBuffer, aPasswd, SEND_BUFFER_SIZE-32);
131     strcat(mSendBuffer, "\r\n");
132     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
133     int bufferLen = strlen(mSendBuffer);
134     int numSent = PSend(mSendBuffer, bufferLen);
135     if (numSent == bufferLen) {
136         PGetSingleLineResponse();
137     }
138     return mStatusCode;
139 }
140 
141 
Quit()142 int DwPopClient::Quit()
143 {
144     mStatusCode = 0;
145     mSingleLineResponse = mMultiLineResponse = "";
146     mLastCommand = kCmdQuit;
147     strcpy(mSendBuffer, "QUIT\r\n");
148     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
149     int bufferLen = strlen(mSendBuffer);
150     int numSent = PSend(mSendBuffer, bufferLen);
151     if (numSent == bufferLen) {
152         PGetSingleLineResponse();
153     }
154     return mStatusCode;
155 }
156 
157 
Stat()158 int DwPopClient::Stat()
159 {
160     mStatusCode = 0;
161     mSingleLineResponse = mMultiLineResponse = "";
162     mLastCommand = kCmdStat;
163     strcpy(mSendBuffer, "STAT\r\n");
164     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
165     int bufferLen = strlen(mSendBuffer);
166     int numSent = PSend(mSendBuffer, bufferLen);
167     if (numSent == bufferLen) {
168         PGetSingleLineResponse();
169     }
170     return mStatusCode;
171 }
172 
173 
List()174 int DwPopClient::List()
175 {
176     mStatusCode = 0;
177     mSingleLineResponse = mMultiLineResponse = "";
178     mLastCommand = kCmdList;
179     strcpy(mSendBuffer, "LIST\r\n");
180     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
181     int bufferLen = strlen(mSendBuffer);
182     int numSent = PSend(mSendBuffer, bufferLen);
183     if (numSent == bufferLen) {
184         PGetSingleLineResponse();
185         if (mStatusCode == '+') {
186             PGetMultiLineResponse();
187         }
188     }
189     return mStatusCode;
190 }
191 
192 
List(int aMsg)193 int DwPopClient::List(int aMsg)
194 {
195     mStatusCode = 0;
196     mSingleLineResponse = mMultiLineResponse = "";
197     mLastCommand = kCmdList;
198     sprintf(mSendBuffer, "LIST %d\r\n", aMsg);
199     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
200     int bufferLen = strlen(mSendBuffer);
201     int numSent = PSend(mSendBuffer, bufferLen);
202     if (numSent == bufferLen) {
203         PGetSingleLineResponse();
204     }
205     return mStatusCode;
206 }
207 
208 
Retr(int aMsg)209 int DwPopClient::Retr(int aMsg)
210 {
211     mStatusCode = 0;
212     mSingleLineResponse = mMultiLineResponse = "";
213     mLastCommand = kCmdRetr;
214     sprintf(mSendBuffer, "RETR %d\r\n", aMsg);
215     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
216     int bufferLen = strlen(mSendBuffer);
217     int numSent = PSend(mSendBuffer, bufferLen);
218     if (numSent == bufferLen) {
219         PGetSingleLineResponse();
220         if (mStatusCode == '+') {
221             PGetMultiLineResponse();
222         }
223     }
224     return mStatusCode;
225 }
226 
227 
Dele(int aMsg)228 int DwPopClient::Dele(int aMsg)
229 {
230     mStatusCode = 0;
231     mSingleLineResponse = mMultiLineResponse = "";
232     mLastCommand = kCmdDele;
233     sprintf(mSendBuffer, "DELE %d\r\n", aMsg);
234     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
235     int bufferLen = strlen(mSendBuffer);
236     int numSent = PSend(mSendBuffer, bufferLen);
237     if (numSent == bufferLen) {
238         PGetSingleLineResponse();
239     }
240     return mStatusCode;
241 }
242 
243 
Noop()244 int DwPopClient::Noop()
245 {
246     mStatusCode = 0;
247     mSingleLineResponse = mMultiLineResponse = "";
248     mLastCommand = kCmdNoop;
249     strcpy(mSendBuffer, "NOOP\r\n");
250     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
251     int bufferLen = strlen(mSendBuffer);
252     int numSent = PSend(mSendBuffer, bufferLen);
253     if (numSent == bufferLen) {
254         PGetSingleLineResponse();
255     }
256     return mStatusCode;
257 }
258 
259 
Rset()260 int DwPopClient::Rset()
261 {
262     mStatusCode = 0;
263     mSingleLineResponse = mMultiLineResponse = "";
264     mLastCommand = kCmdRset;
265     strcpy(mSendBuffer, "RSET\r\n");
266     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
267     int bufferLen = strlen(mSendBuffer);
268     int numSent = PSend(mSendBuffer, bufferLen);
269     if (numSent == bufferLen) {
270         PGetSingleLineResponse();
271     }
272     return mStatusCode;
273 }
274 
275 
Last()276 int DwPopClient::Last()
277 {
278     mStatusCode = 0;
279     mSingleLineResponse = mMultiLineResponse = "";
280     mLastCommand = kCmdRset;
281     strcpy(mSendBuffer, "LAST\r\n");
282     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
283     int bufferLen = strlen(mSendBuffer);
284     int numSent = PSend(mSendBuffer, bufferLen);
285     if (numSent == bufferLen) {
286         PGetSingleLineResponse();
287     }
288     return mStatusCode;
289 }
290 
291 
Apop(const char * aName,const char * aDigest)292 int DwPopClient::Apop(const char* aName, const char* aDigest)
293 {
294     mStatusCode = 0;
295     mSingleLineResponse = mMultiLineResponse = "";
296     mLastCommand = kCmdApop;
297     strcpy(mSendBuffer, "APOP ");
298     strncat(mSendBuffer, aName, 256);
299     strcat(mSendBuffer, " ");
300     strncat(mSendBuffer, aDigest, 256);
301     strcat(mSendBuffer, "\r\n");
302     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
303     int bufferLen = strlen(mSendBuffer);
304     int numSent = PSend(mSendBuffer, bufferLen);
305     if (numSent == bufferLen) {
306         PGetSingleLineResponse();
307     }
308     return mStatusCode;
309 }
310 
311 
Top(int aMsg,int aNumLines)312 int DwPopClient::Top(int aMsg, int aNumLines)
313 {
314     mStatusCode = 0;
315     mSingleLineResponse = mMultiLineResponse = "";
316     mLastCommand = kCmdTop;
317     sprintf(mSendBuffer, "TOP %d %d\r\n", aMsg, aNumLines);
318     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
319     int bufferLen = strlen(mSendBuffer);
320     int numSent = PSend(mSendBuffer, bufferLen);
321     if (numSent == bufferLen) {
322         PGetSingleLineResponse();
323         if (mStatusCode == '+') {
324             PGetMultiLineResponse();
325         }
326     }
327     return mStatusCode;
328 }
329 
330 
Uidl()331 int DwPopClient::Uidl()
332 {
333     mStatusCode = 0;
334     mSingleLineResponse = mMultiLineResponse = "";
335     mLastCommand = kCmdUidl;
336     strcpy(mSendBuffer, "UIDL\r\n");
337     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush;)
338     int bufferLen = strlen(mSendBuffer);
339     int numSent = PSend(mSendBuffer, bufferLen);
340     if (numSent == bufferLen) {
341         PGetSingleLineResponse();
342         if (mStatusCode == '+') {
343             PGetMultiLineResponse();
344         }
345     }
346     return mStatusCode;
347 }
348 
349 
Uidl(int aMsg)350 int DwPopClient::Uidl(int aMsg)
351 {
352     mStatusCode = 0;
353     mSingleLineResponse = mMultiLineResponse = "";
354     mLastCommand = kCmdUidl;
355     sprintf(mSendBuffer, "UIDL %d\r\n", aMsg);
356     DBG_POP_STMT(cout << "C: " << mSendBuffer << flush);
357     int bufferLen = strlen(mSendBuffer);
358     int numSent = PSend(mSendBuffer, bufferLen);
359     if (numSent == bufferLen) {
360         PGetSingleLineResponse();
361         if (mStatusCode == '+') {
362             PGetMultiLineResponse();
363         }
364     }
365     return mStatusCode;
366 }
367 
368 
PGetSingleLineResponse()369 void DwPopClient::PGetSingleLineResponse()
370 {
371     mStatusCode = 0;
372     mSingleLineResponse = "";
373     char* ptr;
374     int len;
375     int err = PGetLine(&ptr, &len);
376     if (! err) {
377         mStatusCode = ptr[0];
378         mSingleLineResponse.assign(ptr, len);
379         DBG_POP_STMT(char buffer[256];)
380         DBG_POP_STMT(strncpy(buffer, ptr, len);)
381         DBG_POP_STMT(buffer[len] = 0;)
382         DBG_POP_STMT(cout << "S: " << buffer;)
383     }
384 }
385 
386 
PGetMultiLineResponse()387 void DwPopClient::PGetMultiLineResponse()
388 {
389     mMultiLineResponse = "";
390 
391     // Get a line at a time until we get CR LF . CR LF
392 
393     while (1) {
394         char* ptr;
395         int len;
396         int err = PGetLine(&ptr, &len);
397 
398         // Check for an error
399 
400         if (err) {
401             mStatusCode = 0;
402             return;
403         }
404 
405         // Check for '.' on a line by itself, which indicates end of multiline
406         // response
407 
408         if (len >= 3 && ptr[0] == '.' && ptr[1] == '\r' && ptr[2] == '\n') {
409             break;
410         }
411 
412         // Remove '.' at beginning of line
413 
414         if (*ptr == '.') ++ptr;
415 
416         // If an observer is assigned, notify it.
417         // Implementation note: An observer is assumed to fetch the multiline
418         // response one line at a time, therefore we assign to the string,
419         // rather than append to it.
420 
421         if (mObserver) {
422             mMultiLineResponse.assign(ptr, len);
423             mObserver->Notify();
424         }
425         else {
426             mMultiLineResponse.append(ptr, len);
427         }
428     }
429 }
430 
431 
PGetLine(char ** aPtr,int * aLen)432 int DwPopClient::PGetLine(char** aPtr, int* aLen)
433 {
434     // Restore the saved state
435 
436     int startPos = mRecvBufferPos;
437     int pos = mRecvBufferPos;
438     int lastChar = -1;
439 
440     // Keep trying until we get a complete line, detect an error, or
441     // determine that the connection has been closed
442 
443     int isEndOfLineFound = 0;
444     while (1) {
445 
446         // Search buffer for end of line chars. Stop when we find them or when
447         // we exhaust the buffer.
448 
449         while (pos < mNumRecvBufferChars) {
450             if (lastChar == '\r' && mRecvBuffer[pos] == '\n') {
451                 isEndOfLineFound = 1;
452                 ++pos;
453                 break;
454             }
455             lastChar = mRecvBuffer[pos];
456             ++pos;
457         }
458         if (isEndOfLineFound) {
459             *aPtr = &mRecvBuffer[startPos];
460             *aLen = pos - startPos;
461             mRecvBufferPos = pos;
462             return 0;
463         }
464 
465         // If the buffer has no room, return without an error; otherwise,
466         // replenish the buffer.
467 
468         // Implementation note: The standard does not allow long lines,
469         // however, that does not mean that they won't occur.  The way
470         // this function deals with long lines is to return a full buffer's
471         // worth of characters as a line.  The next call to this function
472         // will start where this call left off.  In essence, we have
473         // *forced* a line break, but without putting in CR LF characters.
474 
475         if (startPos == 0 && pos == RECV_BUFFER_SIZE) {
476             *aPtr = mRecvBuffer;
477             *aLen = RECV_BUFFER_SIZE;
478             mRecvBufferPos = pos;
479             return 0;
480         }
481         memmove(mRecvBuffer, &mRecvBuffer[startPos],
482             mNumRecvBufferChars-startPos);
483         mNumRecvBufferChars -= startPos;
484         mRecvBufferPos = mNumRecvBufferChars;
485         int bufFreeSpace = RECV_BUFFER_SIZE - mRecvBufferPos;
486         int n = PReceive(&mRecvBuffer[mRecvBufferPos], bufFreeSpace);
487         if (n == 0) {
488             // The connection has been closed or an error occurred
489             return -1;
490         }
491         mNumRecvBufferChars += n;
492         startPos = 0;
493         pos = mRecvBufferPos;
494     }
495 }
496