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